diff --git a/DisCatSharp.ApplicationCommands/DisCatSharp.ApplicationCommands.csproj b/DisCatSharp.ApplicationCommands/DisCatSharp.ApplicationCommands.csproj
index 5e1548d458..3f3fe3e3dd 100644
--- a/DisCatSharp.ApplicationCommands/DisCatSharp.ApplicationCommands.csproj
+++ b/DisCatSharp.ApplicationCommands/DisCatSharp.ApplicationCommands.csproj
@@ -28,6 +28,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -36,6 +37,7 @@
+
diff --git a/DisCatSharp.CommandsNext/CommandsNextExtension.cs b/DisCatSharp.CommandsNext/CommandsNextExtension.cs
index 5955d8e5ec..643b9876e4 100644
--- a/DisCatSharp.CommandsNext/CommandsNextExtension.cs
+++ b/DisCatSharp.CommandsNext/CommandsNextExtension.cs
@@ -380,7 +380,7 @@ public async Task DefaultHelpAsync(CommandContext ctx, [Description("Command to
var helpMessage = helpBuilder.Build();
- var builder = new DiscordMessageBuilder().WithContent(helpMessage.Content).WithEmbed(helpMessage.Embed);
+ var builder = new DiscordMessageBuilder().WithContent(helpMessage.Content).AddEmbed(helpMessage.Embed);
if (!ctx.Config.DmHelp || ctx.Channel is DiscordDmChannel || ctx.Guild == null)
await ctx.RespondAsync(builder).ConfigureAwait(false);
@@ -531,7 +531,7 @@ public CommandContext CreateContext(DiscordMessage msg, string prefix, Command c
ChannelId = msg.ChannelId
};
- if (cmd != null && (cmd.Module is TransientCommandModule || cmd.Module == null))
+ if (cmd != null && cmd.Module is TransientCommandModule or null)
{
var scope = ctx.Services.CreateScope();
ctx.ServiceScopeContext = new(ctx.Services, scope);
diff --git a/DisCatSharp.CommandsNext/DisCatSharp.CommandsNext.csproj b/DisCatSharp.CommandsNext/DisCatSharp.CommandsNext.csproj
index 8a344139c5..f1b603fcdd 100644
--- a/DisCatSharp.CommandsNext/DisCatSharp.CommandsNext.csproj
+++ b/DisCatSharp.CommandsNext/DisCatSharp.CommandsNext.csproj
@@ -34,6 +34,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -45,6 +46,7 @@
+
diff --git a/DisCatSharp.Common/DisCatSharp.Common.csproj b/DisCatSharp.Common/DisCatSharp.Common.csproj
index 4a2ba816c8..2a4811024f 100644
--- a/DisCatSharp.Common/DisCatSharp.Common.csproj
+++ b/DisCatSharp.Common/DisCatSharp.Common.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj
index 1622ec7c60..eb1ec0ccbd 100644
--- a/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj
+++ b/DisCatSharp.Configuration/DisCatSharp.Configuration.csproj
@@ -23,6 +23,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -32,6 +33,7 @@
+
diff --git a/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj b/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj
index 57369fdd55..57b8effc8c 100644
--- a/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj
+++ b/DisCatSharp.Experimental/DisCatSharp.Experimental.csproj
@@ -26,6 +26,7 @@
+
@@ -34,6 +35,7 @@
+
diff --git a/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj b/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj
index 9751af62ff..f646484d8d 100644
--- a/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj
+++ b/DisCatSharp.Hosting.DependencyInjection/DisCatSharp.Hosting.DependencyInjection.csproj
@@ -17,6 +17,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -24,6 +25,7 @@
+
diff --git a/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj b/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj
index 2d9090aaa0..32888a36d4 100644
--- a/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj
+++ b/DisCatSharp.Hosting/DisCatSharp.Hosting.csproj
@@ -22,6 +22,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -34,6 +35,7 @@
+
diff --git a/DisCatSharp.Interactivity/DisCatSharp.Interactivity.csproj b/DisCatSharp.Interactivity/DisCatSharp.Interactivity.csproj
index 10f4fe6ab8..dae9ca1345 100644
--- a/DisCatSharp.Interactivity/DisCatSharp.Interactivity.csproj
+++ b/DisCatSharp.Interactivity/DisCatSharp.Interactivity.csproj
@@ -28,6 +28,7 @@
+
all
@@ -36,6 +37,7 @@
+
diff --git a/DisCatSharp.Interactivity/EventHandling/Paginator.cs b/DisCatSharp.Interactivity/EventHandling/Paginator.cs
index 5600978159..9907455897 100644
--- a/DisCatSharp.Interactivity/EventHandling/Paginator.cs
+++ b/DisCatSharp.Interactivity/EventHandling/Paginator.cs
@@ -249,7 +249,7 @@ private async Task PaginateAsync(IPaginationRequest p, DiscordEmoji emoji)
var page = await p.GetPageAsync().ConfigureAwait(false);
var builder = new DiscordMessageBuilder()
.WithContent(page.Content)
- .WithEmbed(page.Embed);
+ .AddEmbed(page.Embed);
await builder.ModifyAsync(msg).ConfigureAwait(false);
}
diff --git a/DisCatSharp.Interactivity/InteractivityExtension.cs b/DisCatSharp.Interactivity/InteractivityExtension.cs
index 7c6c68da67..436c15eb7f 100644
--- a/DisCatSharp.Interactivity/InteractivityExtension.cs
+++ b/DisCatSharp.Interactivity/InteractivityExtension.cs
@@ -746,7 +746,7 @@ public async Task SendPaginatedMessageAsync(
var builder = new DiscordMessageBuilder()
.WithContent(pages.First().Content)
- .WithEmbed(pages.First().Embed)
+ .AddEmbed(pages.First().Embed)
.AddComponents(bts.ButtonArray);
var message = await builder.SendAsync(channel).ConfigureAwait(false);
@@ -830,7 +830,7 @@ public async Task SendPaginatedMessageAsync(
{
var builder = new DiscordMessageBuilder()
.WithContent(pages.First().Content)
- .WithEmbed(pages.First().Embed);
+ .AddEmbed(pages.First().Embed);
var m = await builder.SendAsync(channel).ConfigureAwait(false);
var timeout = timeoutOverride ?? this.Config.Timeout;
diff --git a/DisCatSharp.Lavalink/DisCatSharp.Lavalink.csproj b/DisCatSharp.Lavalink/DisCatSharp.Lavalink.csproj
index 1feb517eb8..dd8eedd822 100644
--- a/DisCatSharp.Lavalink/DisCatSharp.Lavalink.csproj
+++ b/DisCatSharp.Lavalink/DisCatSharp.Lavalink.csproj
@@ -36,6 +36,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -47,6 +48,7 @@
+
diff --git a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj
index caeba16893..94f4ee9c48 100644
--- a/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj
+++ b/DisCatSharp.Tools/DisCatSharp.Analyzer/DisCatSharp.Analyzer/DisCatSharp.Analyzer.csproj
@@ -37,12 +37,14 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj b/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj
index 4d3d8bc5c9..29b99f6eae 100644
--- a/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj
+++ b/DisCatSharp.VoiceNext/DisCatSharp.VoiceNext.csproj
@@ -29,6 +29,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -36,6 +37,7 @@
+
diff --git a/DisCatSharp/DisCatSharp.csproj b/DisCatSharp/DisCatSharp.csproj
index 162fad0b7a..19456d12e0 100644
--- a/DisCatSharp/DisCatSharp.csproj
+++ b/DisCatSharp/DisCatSharp.csproj
@@ -26,7 +26,7 @@
-
+
@@ -56,12 +56,6 @@
-
-
-
-
-
-
diff --git a/DisCatSharp/Entities/Core/DisCatSharpBuilder.cs b/DisCatSharp/Entities/Core/DisCatSharpBuilder.cs
new file mode 100644
index 0000000000..7dabd46d16
--- /dev/null
+++ b/DisCatSharp/Entities/Core/DisCatSharpBuilder.cs
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+
+namespace DisCatSharp.Entities.Core;
+
+///
+/// Represents the common base for most builders.
+///
+public class DisCatSharpBuilder
+{
+ ///
+ /// The attachments of this builder.
+ ///
+ internal List AttachmentsInternal { get; } = [];
+
+ ///
+ /// The components of this builder.
+ ///
+ internal List ComponentsInternal { get; } = [];
+
+ ///
+ /// The embeds of this builder.
+ ///
+ internal List EmbedsInternal { get; } = [];
+
+ ///
+ /// The files of this builder.
+ ///
+ internal List FilesInternal { get; } = [];
+
+ ///
+ /// The allowed mentions of this builder.
+ ///
+ internal List MentionsInternal { get; } = [];
+
+ ///
+ /// The content of this builder.
+ ///
+ internal string? ContentInternal { get; set; }
+
+ ///
+ /// Whether flags were changed in this builder.
+ ///
+ internal bool FlagsChanged { get; set; } = false;
+
+ ///
+ /// Sets the content of this builder.
+ ///
+ public string? Content
+ {
+ get => this.ContentInternal;
+ set
+ {
+ if (this.IsComponentsV2 || this.IsVoiceMessage)
+ throw new InvalidOperationException("You cannot set the content for UI Kit / Voice messages");
+
+ if (value is { Length: > 2000 })
+ throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
+
+ this.ContentInternal = value;
+ }
+ }
+
+ ///
+ /// Gets the components of this builder.
+ ///
+ public IReadOnlyList Components
+ => this.ComponentsInternal;
+
+ ///
+ /// Gets the embeds of this builder.
+ ///
+ public IReadOnlyList Embeds
+ => this.EmbedsInternal;
+
+ ///
+ /// Gets the attachments of this builder.
+ ///
+ public IReadOnlyList Attachments
+ => this.AttachmentsInternal;
+
+ ///
+ /// Gets the files of this builder.
+ ///
+ public IReadOnlyList Files
+ => this.FilesInternal;
+
+ ///
+ /// Gets the allowed mentions of this builder.
+ ///
+ public IReadOnlyList Mentions
+ => this.MentionsInternal;
+
+ ///
+ /// Sets whether this builder sends a voice message.
+ /// You can't use that on your own, it needs DisCatSharp.Experimental.
+ ///
+ internal bool IsVoiceMessage
+ {
+ get => this.VOICE_MSG;
+ set
+ {
+ this.VOICE_MSG = value;
+ this.FlagsChanged = true;
+ }
+ }
+
+ ///
+ /// Whether this builder sends a voice message.
+ ///
+ private bool VOICE_MSG { get; set; }
+
+ ///
+ /// Sets whether this builder should send a silent message.
+ ///
+ public bool NotificationsSuppressed
+ {
+ get => this.NOTIFICATIONS_SUPPRESSED;
+ set
+ {
+ this.NOTIFICATIONS_SUPPRESSED = value;
+ this.FlagsChanged = true;
+ }
+ }
+
+ ///
+ /// Whether this builder sends a silent message.
+ ///
+ private bool NOTIFICATIONS_SUPPRESSED { get; set; }
+
+ ///
+ /// Sets whether this builder should be using UI Kit.
+ ///
+ public bool IsComponentsV2
+ {
+ get => this.IS_COMPONENTS_V2;
+ set
+ {
+ this.IS_COMPONENTS_V2 = value;
+ this.FlagsChanged = true;
+ }
+ }
+
+ ///
+ /// Whether this builder is using UI Kit.
+ ///
+ private bool IS_COMPONENTS_V2 { get; set; }
+
+ ///
+ /// Sets whether this builder suppresses its embeds.
+ ///
+ public bool EmbedsSuppressed
+ {
+ get => this.EMBEDS_SUPPRESSED;
+ set
+ {
+ if (this.IsComponentsV2)
+ throw new InvalidOperationException("You cannot set embeds suppressed for UI Kit messages since they cannot have embeds");
+
+ this.EMBEDS_SUPPRESSED = value;
+ this.FlagsChanged = true;
+ }
+ }
+
+ ///
+ /// Whether this builder has its embeds suppressed.
+ ///
+ private bool EMBEDS_SUPPRESSED { get; set; }
+
+ ///
+ /// Clears the components on this builder.
+ ///
+ public void ClearComponents()
+ => this.ComponentsInternal.Clear();
+
+ ///
+ /// Allows for clearing the builder so that it can be used again.
+ ///
+ public virtual void Clear()
+ {
+ this.Content = null;
+ this.FilesInternal.Clear();
+ this.EmbedsInternal.Clear();
+ this.AttachmentsInternal.Clear();
+ this.ComponentsInternal.Clear();
+ this.IsVoiceMessage = false;
+ this.IsComponentsV2 = false;
+ this.EmbedsSuppressed = false;
+ this.NotificationsSuppressed = false;
+ this.FlagsChanged = false;
+ this.MentionsInternal.Clear();
+ }
+
+ ///
+ /// Validates the builder.
+ ///
+ internal virtual void Validate()
+ { }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs b/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
index ee2508d5dd..7d011b9c5b 100644
--- a/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
+++ b/DisCatSharp/Entities/Interaction/Components/DiscordComponent.cs
@@ -24,8 +24,14 @@ internal DiscordComponent()
public ComponentType Type { get; internal set; }
///
- /// The Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
+ /// The custom Id of this component, if applicable. Not applicable on ActionRow(s) and link buttons.
///
[JsonProperty("custom_id", NullValueHandling = NullValueHandling.Ignore)]
- public string CustomId { get; internal set; }
+ public string? CustomId { get; internal set; }
+
+ ///
+ /// Gets the Id of the compenent. Determined by Discord.
+ ///
+ [JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
+ public uint Id { get; internal set; }
}
diff --git a/DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs b/DisCatSharp/Entities/Interaction/Components/DiscordComponentEmoji.cs
similarity index 100%
rename from DisCatSharp/Entities/Interaction/Components/DiscordEmojiComponent.cs
rename to DisCatSharp/Entities/Interaction/Components/DiscordComponentEmoji.cs
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordContainerComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordContainerComponent.cs
new file mode 100644
index 0000000000..c4d4ea6a93
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordContainerComponent.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a container component.
+///
+public sealed class DiscordContainerComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordContainerComponent()
+ {
+ this.Type = ComponentType.Container;
+ }
+
+ ///
+ /// Constructs a new container component based on another container component.
+ ///
+ /// The container component to copy.
+ public DiscordContainerComponent(DiscordContainerComponent other)
+ : this()
+ {
+ this.Components = other.Components;
+ this.AccentColor = other.AccentColor;
+ this.Spoiler = other.Spoiler;
+ }
+
+ ///
+ /// Constructs a new container component field with the specified options.
+ ///
+ /// The container components. Max of 10.
+ /// Whether the container should be marked as spoiler.
+ /// The accent color for the container.
+ public DiscordContainerComponent(IEnumerable components, bool? spoiler = null, DiscordColor? accentColor = null)
+ : this()
+ {
+ var comps = components.ToList();
+ if (comps.Count > 10)
+ throw new ArgumentException("You can only have up to 10 components in a container.");
+
+ List allowedTypes = [ComponentType.ActionRow, ComponentType.TextDisplay, ComponentType.Section, ComponentType.MediaGallery, ComponentType.Separator, ComponentType.File];
+ if (comps.Any(c => !allowedTypes.Contains(c.Type)))
+ throw new ArgumentException("All components must be of type ActionRow, TextDisplay, Section, MediaGallery, Separator, or File.");
+
+ this.Components = [.. comps];
+ this.Spoiler = spoiler;
+ this.AccentColor = accentColor;
+ }
+
+ ///
+ /// The components for the container.
+ ///
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList Components { get; internal set; } = [];
+
+ ///
+ /// The accent color for the container.
+ ///
+ [JsonIgnore]
+ public DiscordColor? AccentColor { get; internal set; }
+
+ ///
+ /// Gets the accent color int for the container.
+ ///
+ [JsonProperty("accent_color", NullValueHandling = NullValueHandling.Ignore)]
+ internal int? AccentColorInt
+ => this.AccentColor?.Value;
+
+ ///
+ /// Gets whether this container should be marked as spoiler.
+ ///
+ [JsonProperty("spoiler", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Spoiler { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordFileDisplayComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordFileDisplayComponent.cs
new file mode 100644
index 0000000000..0ccf45d9b9
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordFileDisplayComponent.cs
@@ -0,0 +1,54 @@
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a file display component.
+///
+public sealed class DiscordFileDisplayComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordFileDisplayComponent()
+ {
+ this.Type = ComponentType.File;
+ }
+
+ ///
+ /// Constructs a new file display component based on another file display component.
+ ///
+ /// The file display component to copy.
+ public DiscordFileDisplayComponent(DiscordFileDisplayComponent other)
+ : this()
+ {
+ this.File = other.File;
+ this.Spoiler = other.Spoiler;
+ }
+
+ ///
+ /// Constructs a new file display component field with the specified options.
+ ///
+ /// The file url.
+ /// Whether this file should be marked as spoiler.
+ public DiscordFileDisplayComponent(string url, bool? spoiler)
+ : this()
+ {
+ this.File = new(url);
+ this.Spoiler = spoiler;
+ }
+
+ ///
+ /// Gets the file.
+ ///
+ [JsonProperty("media")]
+ public DiscordUnfurledMediaItem File { get; internal set; }
+
+ ///
+ /// Gets whether this file should be marked as spoiler.
+ ///
+ [JsonProperty("spoiler", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Spoiler { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryComponent.cs
new file mode 100644
index 0000000000..04d6d74e16
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryComponent.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a media gallery component.
+///
+public sealed class DiscordMediaGalleryComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordMediaGalleryComponent()
+ {
+ this.Type = ComponentType.MediaGallery;
+ }
+
+ ///
+ /// Constructs a new media gallery component based on another media gallery component.
+ ///
+ /// The media gallery component to copy.
+ public DiscordMediaGalleryComponent(DiscordMediaGalleryComponent other)
+ : this()
+ {
+ this.Items = other.Items;
+ }
+
+ ///
+ /// Constructs a new media gallery component field with the specified options.
+ ///
+ /// The media gallery items.
+ public DiscordMediaGalleryComponent(IEnumerable items)
+ : this()
+ {
+ var it = items.ToList();
+ if (it.Count > 10)
+ throw new ArgumentException("You can only have up to 10 items in a media gallery.");
+
+ this.Items = it.ToList();
+ }
+
+ ///
+ /// The content for the media gallery.
+ ///
+ [JsonProperty("items", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList Items { get; internal set; } = [];
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryItem.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryItem.cs
new file mode 100644
index 0000000000..c11ec2c823
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordMediaGalleryItem.cs
@@ -0,0 +1,46 @@
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a media gallery item.
+///
+public sealed class DiscordMediaGalleryItem
+{
+ ///
+ /// Constructs a new empty .
+ ///
+ internal DiscordMediaGalleryItem()
+ { }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The url.
+ /// The description.
+ /// Whether this item should be marked as spoiler.
+ public DiscordMediaGalleryItem(string url, string? description = null, bool? spoiler = null)
+ {
+ this.Media = new(url);
+ this.Description = description;
+ this.Spoiler = spoiler;
+ }
+
+ ///
+ /// Gets the media item.
+ ///
+ [JsonProperty("media")]
+ public DiscordUnfurledMediaItem Media { get; internal set; }
+
+ ///
+ /// Gets the description.
+ ///
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string? Description { get; internal set; }
+
+ ///
+ /// Gets whether this gallery item should be marked as spoiler.
+ ///
+ [JsonProperty("spoiler", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Spoiler { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionAccessory.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionAccessory.cs
new file mode 100644
index 0000000000..848177b235
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionAccessory.cs
@@ -0,0 +1,3 @@
+namespace DisCatSharp.Entities;
+
+public class DiscordSectionAccessory : DiscordComponent;
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionComponent.cs
new file mode 100644
index 0000000000..a9ac01ece8
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSectionComponent.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a section component.
+///
+public sealed class DiscordSectionComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordSectionComponent()
+ {
+ this.Type = ComponentType.Section;
+ }
+
+ ///
+ /// Constructs a new section component based on another section component.
+ ///
+ /// The section component to copy.
+ public DiscordSectionComponent(DiscordSectionComponent other)
+ : this()
+ {
+ this.Components = other.Components;
+ this.Accessory = other.Accessory;
+ }
+
+ ///
+ /// Constructs a new section component field with the specified options.
+ ///
+ /// The section components. Max of 3.
+ public DiscordSectionComponent(IEnumerable components)
+ : this()
+ {
+ var comps = components.ToList();
+ if (comps.Count > 3)
+ throw new ArgumentException("You can only have up to 3 components in a section.");
+
+ this.Components = comps.ToList();
+ }
+
+ ///
+ /// The components for the section.
+ ///
+ [JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
+ public IReadOnlyList Components { get; internal set; } = [];
+
+ ///
+ /// The accessory for the section.
+ /// Can be at the moment, but might include buttons later.
+ ///
+ [JsonProperty("accessory", NullValueHandling = NullValueHandling.Ignore)]
+ public DiscordSectionAccessory? Accessory { get; internal set; }
+
+ ///
+ /// Adds a thumbnail component to the section.
+ ///
+ /// The thumbnail url.
+ /// The description of the thumbnail.
+ /// Whether this thumbnail should be marked as spoiler.
+ /// The current .
+ public DiscordSectionComponent WithThumbnailComponent(string url, string? description = null, bool? spoiler = null)
+ {
+ this.Accessory = new DiscordThumnailComponent(url, description, spoiler);
+ return this;
+ }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSeparatorComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSeparatorComponent.cs
new file mode 100644
index 0000000000..0630e0d5f1
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordSeparatorComponent.cs
@@ -0,0 +1,57 @@
+using System;
+
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a separator component.
+///
+public sealed class DiscordSeparatorComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordSeparatorComponent()
+ {
+ this.Type = ComponentType.Separator;
+ }
+
+ ///
+ /// Constructs a new separator component based on another separator component.
+ ///
+ /// The button to copy.
+ public DiscordSeparatorComponent(DiscordSeparatorComponent other)
+ : this()
+ {
+ this.Divider = other.Divider;
+ this.Spacing = other.Spacing;
+ }
+
+ ///
+ /// Constructs a new separator component field with the specified options.
+ ///
+ /// Whether this is a divider.
+ /// The spacing size.
+ /// Is thrown when no label is set.
+ public DiscordSeparatorComponent(bool? divider = null, SeparatorSpacingSize? spacing = null)
+ : this()
+ {
+ this.Divider = divider;
+ this.Spacing = spacing;
+ }
+
+ ///
+ /// The spacing size.
+ ///
+ [JsonProperty("spacing", NullValueHandling = NullValueHandling.Ignore)]
+ public SeparatorSpacingSize? Spacing { get; set; }
+
+ ///
+ /// Whether this is a divider.
+ ///
+ [JsonProperty("divider", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Divider { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordTextDisplayComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordTextDisplayComponent.cs
new file mode 100644
index 0000000000..9d97dd0992
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordTextDisplayComponent.cs
@@ -0,0 +1,45 @@
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a text display component.
+///
+public sealed class DiscordTextDisplayComponent : DiscordComponent
+{
+ ///
+ /// Constructs a new .
+ ///
+ internal DiscordTextDisplayComponent()
+ {
+ this.Type = ComponentType.TextDisplay;
+ }
+
+ ///
+ /// Constructs a new text display component based on another text display component.
+ ///
+ /// The text display component to copy.
+ public DiscordTextDisplayComponent(DiscordTextDisplayComponent other)
+ : this()
+ {
+ this.Content = other.Content;
+ }
+
+ ///
+ /// Constructs a new text display component field with the specified options.
+ ///
+ /// The content for the text display.
+ public DiscordTextDisplayComponent(string content)
+ : this()
+ {
+ this.Content = content;
+ }
+
+ ///
+ /// The content for the text display.
+ ///
+ [JsonProperty("content")]
+ public string Content { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordThumnailComponent.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordThumnailComponent.cs
new file mode 100644
index 0000000000..11b4aa1408
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordThumnailComponent.cs
@@ -0,0 +1,51 @@
+using DisCatSharp.Enums;
+
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a thumbnail component.
+///
+public sealed class DiscordThumnailComponent : DiscordSectionAccessory
+{
+ ///
+ /// Constructs a new empty .
+ ///
+ internal DiscordThumnailComponent()
+ {
+ this.Type = ComponentType.Thumbnail;
+ }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The thumbnail url.
+ /// The description of the thumbnail.
+ /// Whether this thumbnail should be marked as spoiler.
+ internal DiscordThumnailComponent(string url, string? description = null, bool? spoiler = null)
+ : this()
+ {
+ this.Media = new(url);
+ this.Description = description;
+ this.Spoiler = spoiler;
+ }
+
+ ///
+ /// Gets the media item.
+ ///
+ [JsonProperty("media")]
+ public DiscordUnfurledMediaItem Media { get; internal set; }
+
+ ///
+ /// Gets the description.
+ ///
+ [JsonProperty("description", NullValueHandling = NullValueHandling.Ignore)]
+ public string? Description { get; internal set; }
+
+ ///
+ /// Gets whether this gallery item should be marked as spoiler.
+ ///
+ [JsonProperty("spoiler", NullValueHandling = NullValueHandling.Ignore)]
+ public bool? Spoiler { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordUnfurledMediaItem.cs b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordUnfurledMediaItem.cs
new file mode 100644
index 0000000000..920ebc7c5a
--- /dev/null
+++ b/DisCatSharp/Entities/Interaction/Components/V2Components/DiscordUnfurledMediaItem.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json;
+
+namespace DisCatSharp.Entities;
+
+///
+/// Represents a media item.
+///
+public sealed class DiscordUnfurledMediaItem
+{
+ ///
+ /// Constructs a new empty .
+ ///
+ internal DiscordUnfurledMediaItem()
+ { }
+
+ ///
+ /// Constructs a new .
+ ///
+ /// The items url.
+ internal DiscordUnfurledMediaItem(string url)
+ {
+ this.Url = url;
+ }
+
+ ///
+ /// Gets the url.
+ ///
+ [JsonProperty("url")]
+ public string Url { get; internal set; }
+}
diff --git a/DisCatSharp/Entities/Interaction/DiscordCallbackHintBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordCallbackHintBuilder.cs
index 462ff8a43e..c299bdb7c1 100644
--- a/DisCatSharp/Entities/Interaction/DiscordCallbackHintBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordCallbackHintBuilder.cs
@@ -7,12 +7,12 @@ namespace DisCatSharp.Entities;
///
/// Represents a callback hint builder for an interaction.
///
-public sealed class DiscordCallbackHintBuilder
+internal sealed class DiscordCallbackHintBuilder
{
///
/// Constructs a new .
///
- public DiscordCallbackHintBuilder()
+ internal DiscordCallbackHintBuilder()
{
this.Clear();
}
@@ -21,7 +21,7 @@ public DiscordCallbackHintBuilder()
/// Constructs a new from an existing one.
///
/// The existing .
- public DiscordCallbackHintBuilder(DiscordCallbackHintBuilder other)
+ internal DiscordCallbackHintBuilder(DiscordCallbackHintBuilder other)
{
this.Clear();
this.CallbackHints.AddRange(other.CallbackHints);
@@ -30,7 +30,7 @@ public DiscordCallbackHintBuilder(DiscordCallbackHintBuilder other)
///
/// Gets the callback hints.
///
- public List CallbackHints { get; internal set; } = [];
+ internal List CallbackHints { get; set; } = [];
///
/// Adds a callback hint to the builder.
@@ -39,7 +39,7 @@ public DiscordCallbackHintBuilder(DiscordCallbackHintBuilder other)
/// The intended use of ephemeral. Required if it's only ephemeral.
/// The intended required permissions.
/// The updated .
- public DiscordCallbackHintBuilder AddCallbackHint(InteractionResponseType intendedCallbackType, InteractionCallbackEphemerality intendedCallbackEphemerality = InteractionCallbackEphemerality.Optional, Permissions? intendedRequiredPermissions = null)
+ internal DiscordCallbackHintBuilder AddCallbackHint(InteractionResponseType intendedCallbackType, InteractionCallbackEphemerality intendedCallbackEphemerality = InteractionCallbackEphemerality.Optional, Permissions? intendedRequiredPermissions = null)
{
this.CallbackHints.Add(new()
{
@@ -53,6 +53,6 @@ public DiscordCallbackHintBuilder AddCallbackHint(InteractionResponseType intend
///
/// Clears the callback hints.
///
- public void Clear()
+ internal void Clear()
=> this.CallbackHints.Clear();
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
index f8a164e7fe..5542120fa2 100644
--- a/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordFollowupMessageBuilder.cs
@@ -3,28 +3,15 @@
using System.IO;
using System.Linq;
+using DisCatSharp.Entities.Core;
+
namespace DisCatSharp.Entities;
///
/// Constructs a followup message to an interaction.
///
-public sealed class DiscordFollowupMessageBuilder
+public sealed class DiscordFollowupMessageBuilder : DisCatSharpBuilder
{
- private readonly List _components = [];
-
- private readonly List _embeds = [];
-
- private readonly List _files = [];
-
- internal readonly List AttachmentsInternal = [];
-
- private string _content;
-
- ///
- /// Whether flags were changed.
- ///
- internal bool FlagsChanged = false;
-
///
/// Whether this followup message is text-to-speech.
///
@@ -46,96 +33,20 @@ public bool IsEphemeral
private bool EPH { get; set; }
///
- /// Whether to suppress embeds.
- ///
- public bool EmbedsSuppressed
- {
- get => this.EMB_SUP;
- set
- {
- this.EMB_SUP = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool EMB_SUP { get; set; }
-
- ///
- /// Whether to send as voice message.
- /// You can't use that on your own, it needs DisCatSharp.Experimental.
- ///
- internal bool IsVoiceMessage
- {
- get => this.VOICE_MSG;
- set
- {
- this.VOICE_MSG = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool VOICE_MSG { get; set; }
-
- ///
- /// Whether to send as silent message.
+ /// Gets the poll for this message.
///
- public bool NotificationsSuppressed
- {
- get => this.NOTI_SUP;
- set
- {
- this.NOTI_SUP = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool NOTI_SUP { get; set; }
+ public DiscordPollBuilder? Poll { get; private set; }
///
- /// Message to send on followup message.
+ /// Sets that this builder should be using UI Kit.
///
- public string Content
+ /// The current builder to chain calls with.
+ public DiscordFollowupMessageBuilder WithV2Components()
{
- get => this._content;
- set
- {
- if (value is { Length: > 2000 })
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
-
- this._content = value;
- }
+ this.IsComponentsV2 = true;
+ return this;
}
- ///
- /// Embeds to send on followup message.
- ///
- public IReadOnlyList Embeds => this._embeds;
-
- ///
- /// Files to send on this followup message.
- ///
- public IReadOnlyList Files => this._files;
-
- ///
- /// Components to send on this followup message.
- ///
- public IReadOnlyList Components => this._components;
-
- ///
- /// Attachments to be send with this followup request.
- ///
- public IReadOnlyList Attachments => this.AttachmentsInternal;
-
- ///
- /// Mentions to send on this followup message.
- ///
- public List? Mentions { get; private set; }
-
- ///
- /// Gets the poll for this message.
- ///
- public DiscordPollBuilder? Poll { get; private set; }
-
///
/// Appends a collection of components to the message.
///
@@ -154,11 +65,11 @@ public DiscordFollowupMessageBuilder AddComponents(IEnumerable 5)
+ if (ara.Length + this.ComponentsInternal.Count > 5)
throw new ArgumentException("ActionRow count exceeds maximum of five.");
foreach (var ar in ara)
- this._components.Add(ar);
+ this.ComponentsInternal.Add(ar);
return this;
}
@@ -171,14 +82,35 @@ public DiscordFollowupMessageBuilder AddComponents(IEnumerable contained more than 5 components.
public DiscordFollowupMessageBuilder AddComponents(IEnumerable components)
{
- var compArr = components.ToArray();
- var count = compArr.Length;
+ var cmpArr = components.ToArray();
+ var count = cmpArr.Length;
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ if (this.IsComponentsV2)
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 10:
+ throw new ArgumentException("Cannot add more than 10 components!");
+ }
+
+ this.ComponentsInternal.AddRange(cmpArr);
+ }
+ else
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 5:
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
+ }
+
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this.ComponentsInternal.Add(comp);
+ }
- var arc = new DiscordActionRowComponent(compArr);
- this._components.Add(arc);
return this;
}
@@ -222,7 +154,8 @@ public DiscordFollowupMessageBuilder WithContent(string content)
/// The builder to chain calls with.
public DiscordFollowupMessageBuilder AddEmbed(DiscordEmbed embed)
{
- this._embeds.Add(embed);
+ ArgumentNullException.ThrowIfNull(embed, nameof(embed));
+ this.EmbedsInternal.Add(embed);
return this;
}
@@ -233,7 +166,7 @@ public DiscordFollowupMessageBuilder AddEmbed(DiscordEmbed embed)
/// The builder to chain calls with.
public DiscordFollowupMessageBuilder AddEmbeds(IEnumerable embeds)
{
- this._embeds.AddRange(embeds);
+ this.EmbedsInternal.AddRange(embeds);
return this;
}
@@ -250,16 +183,16 @@ public DiscordFollowupMessageBuilder AddEmbeds(IEnumerable embeds)
/// The builder to chain calls with.
public DiscordFollowupMessageBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count >= 10)
+ if (this.FilesInternal.Count >= 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == filename))
+ if (this.FilesInternal.Any(x => x.Filename == filename))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(filename, data, data.Position, description: description));
+ this.FilesInternal.Add(new(filename, data, data.Position, description: description));
else
- this._files.Add(new(filename, data, null, description: description));
+ this.FilesInternal.Add(new(filename, data, null, description: description));
return this;
}
@@ -276,16 +209,16 @@ public DiscordFollowupMessageBuilder AddFile(string filename, Stream data, bool
/// The builder to chain calls with.
public DiscordFollowupMessageBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count >= 10)
+ if (this.FilesInternal.Count >= 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == stream.Name))
+ if (this.FilesInternal.Any(x => x.Filename == stream.Name))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(stream.Name, stream, stream.Position, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, stream.Position, description: description));
else
- this._files.Add(new(stream.Name, stream, null, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, null, description: description));
return this;
}
@@ -301,18 +234,18 @@ public DiscordFollowupMessageBuilder AddFile(FileStream stream, bool resetStream
/// The builder to chain calls with.
public DiscordFollowupMessageBuilder AddFiles(Dictionary files, bool resetStreamPosition = false)
{
- if (this.Files.Count + files.Count > 10)
+ if (this.FilesInternal.Count + files.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
foreach (var file in files)
{
- if (this._files.Any(x => x.Filename == file.Key))
+ if (this.FilesInternal.Any(x => x.Filename == file.Key))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(file.Key, file.Value, file.Value.Position));
+ this.FilesInternal.Add(new(file.Key, file.Value, file.Value.Position));
else
- this._files.Add(new(file.Key, file.Value, null));
+ this.FilesInternal.Add(new(file.Key, file.Value, null));
}
return this;
@@ -321,28 +254,22 @@ public DiscordFollowupMessageBuilder AddFiles(Dictionary files,
///
/// Adds the mention to the mentions to parse, etc. with the followup message.
///
- /// Mention to add.
+ /// Mention to add.
/// The builder to chain calls with.
- public DiscordFollowupMessageBuilder WithAllowedMention(IMention allowedMention)
+ public DiscordFollowupMessageBuilder WithAllowedMention(IMention mention)
{
- if (this.Mentions != null)
- this.Mentions.Add(allowedMention);
- else
- this.Mentions = [allowedMention];
+ this.MentionsInternal.Add(mention);
return this;
}
///
/// Adds the mentions to the mentions to parse, etc. with the followup message.
///
- /// Mentions to add.
+ /// Mentions to add.
/// The builder to chain calls with.
- public DiscordFollowupMessageBuilder WithAllowedMentions(IEnumerable allowedMentions)
+ public DiscordFollowupMessageBuilder WithAllowedMentions(IEnumerable mentions)
{
- if (this.Mentions != null)
- this.Mentions.AddRange(allowedMentions);
- else
- this.Mentions = allowedMentions.ToList();
+ this.MentionsInternal.AddRange(mentions);
return this;
}
@@ -351,7 +278,6 @@ public DiscordFollowupMessageBuilder WithAllowedMentions(IEnumerable a
///
public DiscordFollowupMessageBuilder AsEphemeral()
{
- this.FlagsChanged = true;
this.IsEphemeral = true;
return this;
}
@@ -361,7 +287,6 @@ public DiscordFollowupMessageBuilder AsEphemeral()
///
public DiscordFollowupMessageBuilder SuppressEmbeds()
{
- this.FlagsChanged = true;
this.EmbedsSuppressed = true;
return this;
}
@@ -371,7 +296,6 @@ public DiscordFollowupMessageBuilder SuppressEmbeds()
///
internal DiscordFollowupMessageBuilder AsVoiceMessage(bool asVoiceMessage = true)
{
- this.FlagsChanged = true;
this.IsVoiceMessage = asVoiceMessage;
return this;
}
@@ -381,45 +305,31 @@ internal DiscordFollowupMessageBuilder AsVoiceMessage(bool asVoiceMessage = true
///
public DiscordFollowupMessageBuilder AsSilentMessage()
{
- this.FlagsChanged = true;
this.NotificationsSuppressed = true;
return this;
}
- ///
- /// Clears all message components on this builder.
- ///
- public void ClearComponents()
- => this._components.Clear();
-
///
/// Clears the poll from this builder.
///
public void ClearPoll()
=> this.Poll = null;
- ///
- /// Allows for clearing the Followup Message builder so that it can be used again to send a new message.
- ///
- public void Clear()
+ ///
+ public override void Clear()
{
- this.Content = null;
- this._embeds.Clear();
this.IsTts = false;
- this.Mentions = null;
- this._files.Clear();
this.IsEphemeral = false;
- this._components.Clear();
+ base.Clear();
}
- ///
- /// Validates the builder.
- ///
- internal void Validate()
+ ///
+ internal override void Validate()
{
- if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any() && !this.Components.Any() && this.Poll is null && this?.Attachments.Count == 0)
+ if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && this.EmbedsInternal.Count == 0 && this.ComponentsInternal.Count == 0 && this.Poll is null && this.AttachmentsInternal.Count == 0)
throw new ArgumentException("You must specify content, an embed, a component, a poll, or at least one file.");
this.Poll?.Validate();
+ base.Validate();
}
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
index f8776f5019..4b58349ae9 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionApplicationCommandCallbackData.cs
@@ -33,7 +33,7 @@ internal class DiscordInteractionApplicationCommandCallbackData : ObservableApiO
/// Gets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyList? Mentions { get; internal set; }
+ public DiscordMentions? Mentions { get; internal set; }
///
/// Gets the flags.
@@ -45,7 +45,7 @@ internal class DiscordInteractionApplicationCommandCallbackData : ObservableApiO
/// Gets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection? Components { get; internal set; }
+ public IReadOnlyCollection? Components { get; internal set; }
///
/// Gets the autocomplete choices.
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
index 6ac377b6d3..89d0c832d5 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionModalBuilder.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Linq;
+using DisCatSharp.Entities.Core;
+
namespace DisCatSharp.Entities;
///
@@ -11,7 +13,10 @@ public sealed class DiscordInteractionModalBuilder
{
private readonly List _callbackHints = [];
- private readonly List _components = [];
+ ///
+ /// The components.
+ ///
+ internal readonly List ComponentsInternal = [];
private string _title;
@@ -24,6 +29,11 @@ public DiscordInteractionModalBuilder(string title = null, string customId = nul
this.CustomId = customId ?? Guid.NewGuid().ToString();
}
+ ///
+ /// Components to send. Please use instead.
+ ///
+ public IReadOnlyList Components => this.ComponentsInternal;
+
///
/// Title of modal.
///
@@ -47,7 +57,7 @@ public string Title
///
/// Components to send on this interaction response.
///
- public IReadOnlyList ModalComponents => this._components;
+ public IReadOnlyList ModalComponents => this.Components.Select(c => c as DiscordActionRowComponent).ToList()!;
///
/// The hints to send on this interaction response.
@@ -82,7 +92,7 @@ public DiscordInteractionModalBuilder WithCustomId(string customId)
/// The hint builder.
/// The current builder to chain calls with.
/// Thrown when the is .
- public DiscordInteractionModalBuilder WithCallbackHints(DiscordCallbackHintBuilder hintBuilder)
+ internal DiscordInteractionModalBuilder WithCallbackHints(DiscordCallbackHintBuilder hintBuilder)
{
if (hintBuilder == null)
throw new ArgumentNullException(nameof(hintBuilder), "Callback hint builder cannot be null.");
@@ -142,14 +152,12 @@ public DiscordInteractionModalBuilder AddModalComponents(params DiscordComponent
throw new ArgumentException("You can only add 5 components to modals.");
- if (this._components.Count + ara.Length > 5)
- throw new ArgumentException($"You try to add too many components. We already have {this._components.Count}.");
+ if (this.ComponentsInternal.Count + ara.Length > 5)
+ throw new ArgumentException($"You try to add too many components. We already have {this.ComponentsInternal.Count}.");
foreach (var ar in ara)
- this._components.Add(new(new List
- {
- ar
- }));
+ this.ComponentsInternal.Add(new DiscordActionRowComponent(
+ [ar]));
return this;
}
@@ -164,11 +172,11 @@ public DiscordInteractionModalBuilder AddModalComponents(IEnumerable 5)
+ if (ara.Length + this.ComponentsInternal.Count > 5)
throw new ArgumentException("ActionRow count exceeds maximum of five.");
foreach (var ar in ara)
- this._components.Add(ar);
+ this.ComponentsInternal.Add(ar);
return this;
}
@@ -180,10 +188,8 @@ public DiscordInteractionModalBuilder AddModalComponents(IEnumerableThe current builder to chain calls with.
internal DiscordInteractionModalBuilder AddModalComponents(DiscordComponent component)
{
- this._components.Add(new(new List
- {
- component
- }));
+ this.ComponentsInternal.Add(new DiscordActionRowComponent(
+ [component]));
return this;
}
@@ -192,14 +198,14 @@ internal DiscordInteractionModalBuilder AddModalComponents(DiscordComponent comp
/// Clears all message components on this builder.
///
public void ClearComponents()
- => this._components.Clear();
+ => this.ComponentsInternal.Clear();
///
/// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
///
public void Clear()
{
- this._components.Clear();
+ this.ComponentsInternal.Clear();
this.Title = null!;
this.CustomId = null!;
}
diff --git a/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs b/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
index 66b3182792..1cc1338371 100644
--- a/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
+++ b/DisCatSharp/Entities/Interaction/DiscordInteractionResponseBuilder.cs
@@ -3,31 +3,19 @@
using System.IO;
using System.Linq;
+using DisCatSharp.Entities.Core;
+
namespace DisCatSharp.Entities;
///
/// Constructs an interaction response.
///
-public sealed class DiscordInteractionResponseBuilder
+public sealed class DiscordInteractionResponseBuilder : DisCatSharpBuilder
{
- private readonly List _callbackHints = [];
-
- private readonly List _choices = [];
-
- private readonly List _components = [];
-
- private readonly List _embeds = [];
-
- private readonly List _files = [];
-
- internal readonly List AttachmentsInternal = [];
-
- private string _content;
-
///
- /// Whether flags were changed.
+ /// Gets the callback hints.
///
- internal bool FlagsChanged = false;
+ private readonly List _callbackHints = [];
///
/// Constructs a new empty interaction response builder.
@@ -40,14 +28,23 @@ public DiscordInteractionResponseBuilder()
/// .
///
/// The builder to copy.
- public DiscordInteractionResponseBuilder(DiscordMessageBuilder builder)
+ public DiscordInteractionResponseBuilder(DisCatSharpBuilder builder)
{
- this._content = builder.Content;
- this.Mentions = builder.Mentions;
- this._embeds.AddRange(builder.Embeds);
- this._components.AddRange(builder.Components);
+ this.Content = builder.Content;
+ this.MentionsInternal.AddRange(builder.MentionsInternal);
+ this.EmbedsInternal.AddRange(builder.Embeds);
+ this.ComponentsInternal.AddRange(builder.Components);
+ this.EmbedsSuppressed = builder.EmbedsSuppressed;
+ this.IsComponentsV2 = builder.IsComponentsV2;
+ this.FilesInternal.AddRange(builder.Files);
+ this.AttachmentsInternal.AddRange(builder.Attachments);
}
+ ///
+ /// Gets the choices.
+ ///
+ internal List ChoicesInternal { get; } = [];
+
///
/// Whether this interaction response is text-to-speech.
///
@@ -68,102 +65,18 @@ public bool IsEphemeral
private bool EPH { get; set; }
- ///
- /// Whether to suppress embeds.
- ///
- public bool EmbedsSuppressed
- {
- get => this.EMB_SUP;
- set
- {
- this.EMB_SUP = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool EMB_SUP { get; set; }
-
- ///
- /// Whether to send as silent message.
- ///
- public bool NotificationsSuppressed
- {
- get => this.NOTI_SUP;
- set
- {
- this.NOTI_SUP = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool NOTI_SUP { get; set; }
-
- ///
- /// Whether to send as voice message.
- /// You can't use that on your own, it needs DisCatSharp.Experimental.
- ///
- internal bool IsVoiceMessage
- {
- get => this.VOICE_MSG;
- set
- {
- this.VOICE_MSG = value;
- this.FlagsChanged = true;
- }
- }
-
- private bool VOICE_MSG { get; set; }
-
- ///
- /// Content of the message to send.
- ///
- public string Content
- {
- get => this._content;
- set
- {
- if (value is { Length: > 2000 })
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
-
- this._content = value;
- }
- }
-
- ///
- /// Embeds to send on this interaction response.
- ///
- public IReadOnlyList Embeds => this._embeds;
-
- ///
- /// Files to send on this interaction response.
- ///
- public IReadOnlyList Files => this._files;
-
- ///
- /// Components to send on this interaction response.
- ///
- public IReadOnlyList Components => this._components;
-
///
/// The choices to send on this interaction response.
/// Mutually exclusive with content, embed, and components.
///
- public IReadOnlyList Choices => this._choices;
-
- ///
- /// Attachments to be send with this interaction response.
- ///
- public IReadOnlyList Attachments => this.AttachmentsInternal;
-
- ///
- /// Mentions to send on this interaction response.
- ///
- public List? Mentions { get; private set; }
+ public IReadOnlyList Choices
+ => this.ChoicesInternal;
///
/// The hints to send on this interaction response.
///
- public IReadOnlyList CallbackHints => this._callbackHints;
+ internal IReadOnlyList CallbackHints
+ => this._callbackHints;
///
/// Gets the poll for this message.
@@ -176,7 +89,7 @@ public string Content
/// The hint builder.
/// The current builder to chain calls with.
/// Thrown when the is .
- public DiscordInteractionResponseBuilder WithCallbackHints(DiscordCallbackHintBuilder hintBuilder)
+ internal DiscordInteractionResponseBuilder WithCallbackHints(DiscordCallbackHintBuilder hintBuilder)
{
if (hintBuilder == null)
throw new ArgumentNullException(nameof(hintBuilder), "Callback hint builder cannot be null.");
@@ -208,11 +121,11 @@ public DiscordInteractionResponseBuilder AddComponents(IEnumerable 5)
+ if (ara.Length + this.ComponentsInternal.Count > 5)
throw new ArgumentException("ActionRow count exceeds maximum of five.");
foreach (var ar in ara)
- this._components.Add(ar);
+ this.ComponentsInternal.Add(ar);
return this;
}
@@ -225,14 +138,35 @@ public DiscordInteractionResponseBuilder AddComponents(IEnumerableThrown when passing more than 5 components.
public DiscordInteractionResponseBuilder AddComponents(IEnumerable components)
{
- var compArr = components.ToArray();
- var count = compArr.Length;
+ var cmpArr = components.ToArray();
+ var count = cmpArr.Length;
- if (count > 5)
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ if (this.IsComponentsV2)
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 10:
+ throw new ArgumentException("Cannot add more than 10 components!");
+ }
+
+ this.ComponentsInternal.AddRange(cmpArr);
+ }
+ else
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 5:
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
+ }
+
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this.ComponentsInternal.Add(comp);
+ }
- var arc = new DiscordActionRowComponent(compArr);
- this._components.Add(arc);
return this;
}
@@ -264,18 +198,26 @@ public DiscordInteractionResponseBuilder WithTts(bool tts)
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder AsEphemeral()
{
- this.FlagsChanged = true;
this.IsEphemeral = true;
return this;
}
+ ///
+ /// Sets that this builder should be using UI Kit.
+ ///
+ /// The current builder to chain calls with.
+ public DiscordInteractionResponseBuilder WithV2Components()
+ {
+ this.IsComponentsV2 = true;
+ return this;
+ }
+
///
/// Sets the interaction response to suppress embeds.
///
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder SuppressEmbeds()
{
- this.FlagsChanged = true;
this.EmbedsSuppressed = true;
return this;
}
@@ -286,7 +228,6 @@ public DiscordInteractionResponseBuilder SuppressEmbeds()
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder AsSilentMessage()
{
- this.FlagsChanged = true;
this.NotificationsSuppressed = true;
return this;
}
@@ -296,7 +237,6 @@ public DiscordInteractionResponseBuilder AsSilentMessage()
///
internal DiscordInteractionResponseBuilder AsVoiceMessage(bool asVoiceMessage = true)
{
- this.FlagsChanged = true;
this.IsVoiceMessage = asVoiceMessage;
return this;
}
@@ -319,8 +259,8 @@ public DiscordInteractionResponseBuilder WithContent(string content)
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder AddEmbed(DiscordEmbed embed)
{
- if (embed != null)
- this._embeds.Add(embed); // Interactions will 400 silently //
+ ArgumentNullException.ThrowIfNull(embed, nameof(embed));
+ this.EmbedsInternal.Add(embed);
return this;
}
@@ -331,7 +271,7 @@ public DiscordInteractionResponseBuilder AddEmbed(DiscordEmbed embed)
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder AddEmbeds(IEnumerable embeds)
{
- this._embeds.AddRange(embeds);
+ this.EmbedsInternal.AddRange(embeds);
return this;
}
@@ -348,16 +288,16 @@ public DiscordInteractionResponseBuilder AddEmbeds(IEnumerable emb
/// The builder to chain calls with.
public DiscordInteractionResponseBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count >= 10)
+ if (this.FilesInternal.Count >= 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == filename))
+ if (this.FilesInternal.Any(x => x.Filename == filename))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(filename, data, data.Position, description: description));
+ this.FilesInternal.Add(new(filename, data, data.Position, description: description));
else
- this._files.Add(new(filename, data, null, description: description));
+ this.FilesInternal.Add(new(filename, data, null, description: description));
return this;
}
@@ -374,16 +314,16 @@ public DiscordInteractionResponseBuilder AddFile(string filename, Stream data, b
/// The builder to chain calls with.
public DiscordInteractionResponseBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count >= 10)
+ if (this.FilesInternal.Count >= 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == stream.Name))
+ if (this.FilesInternal.Any(x => x.Filename == stream.Name))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(stream.Name, stream, stream.Position, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, stream.Position, description: description));
else
- this._files.Add(new(stream.Name, stream, null, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, null, description: description));
return this;
}
@@ -399,18 +339,18 @@ public DiscordInteractionResponseBuilder AddFile(FileStream stream, bool resetSt
/// The builder to chain calls with.
public DiscordInteractionResponseBuilder AddFiles(Dictionary files, bool resetStreamPosition = false)
{
- if (this.Files.Count + files.Count > 10)
+ if (this.FilesInternal.Count + files.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
foreach (var file in files)
{
- if (this._files.Any(x => x.Filename == file.Key))
+ if (this.FilesInternal.Any(x => x.Filename == file.Key))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(file.Key, file.Value, file.Value.Position));
+ this.FilesInternal.Add(new(file.Key, file.Value, file.Value.Position));
else
- this._files.Add(new(file.Key, file.Value, null));
+ this.FilesInternal.Add(new(file.Key, file.Value, null));
}
return this;
@@ -419,28 +359,22 @@ public DiscordInteractionResponseBuilder AddFiles(Dictionary fil
///
/// Adds the mention to the mentions to parse, etc. with the interaction response.
///
- /// Mention to add.
+ /// Mention to add.
/// The current builder to chain calls with.
- public DiscordInteractionResponseBuilder WithAllowedMention(IMention allowedMention)
+ public DiscordInteractionResponseBuilder WithAllowedMention(IMention mention)
{
- if (this.Mentions != null)
- this.Mentions.Add(allowedMention);
- else
- this.Mentions = [allowedMention];
+ this.MentionsInternal.Add(mention);
return this;
}
///
/// Adds the mentions to the mentions to parse, etc. with the interaction response.
///
- /// Mentions to add.
+ /// Mentions to add.
/// The current builder to chain calls with.
- public DiscordInteractionResponseBuilder WithAllowedMentions(IEnumerable allowedMentions)
+ public DiscordInteractionResponseBuilder WithAllowedMentions(IEnumerable mentions)
{
- if (this.Mentions != null)
- this.Mentions.AddRange(allowedMentions);
- else
- this.Mentions = allowedMentions.ToList();
+ this.MentionsInternal.AddRange(mentions);
return this;
}
@@ -451,7 +385,7 @@ public DiscordInteractionResponseBuilder WithAllowedMentions(IEnumerableThe current builder to chain calls with.
public DiscordInteractionResponseBuilder AddAutoCompleteChoice(DiscordApplicationCommandAutocompleteChoice choice)
{
- this._choices.Add(choice);
+ this.ChoicesInternal.Add(choice);
return this;
}
@@ -462,7 +396,7 @@ public DiscordInteractionResponseBuilder AddAutoCompleteChoice(DiscordApplicatio
/// The current builder to chain calls with.
public DiscordInteractionResponseBuilder AddAutoCompleteChoices(IEnumerable choices)
{
- this._choices.AddRange(choices);
+ this.ChoicesInternal.AddRange(choices);
return this;
}
@@ -474,34 +408,18 @@ public DiscordInteractionResponseBuilder AddAutoCompleteChoices(IEnumerable this.AddAutoCompleteChoices((IEnumerable)choices);
- ///
- /// Clears all message components on this builder.
- ///
- public void ClearComponents()
- => this._components.Clear();
-
///
/// Clears the poll from this builder.
///
public void ClearPoll()
=> this.Poll = null;
- ///
- /// Allows for clearing the Interaction Response Builder so that it can be used again to send a new response.
- ///
- public void Clear()
+ ///
+ public override void Clear()
{
- this.Content = null;
- this._embeds.Clear();
- this.IsTts = false;
this.IsEphemeral = false;
- this.Mentions = null;
- this._components.Clear();
- this._choices.Clear();
- this._files.Clear();
+ this.ChoicesInternal.Clear();
this.Poll = null;
- this.IsVoiceMessage = false;
- this.NotificationsSuppressed = false;
- this.EmbedsSuppressed = false;
+ base.Clear();
}
}
diff --git a/DisCatSharp/Entities/Message/DiscordMessage.cs b/DisCatSharp/Entities/Message/DiscordMessage.cs
index d8a4f59c4d..7110a3e395 100644
--- a/DisCatSharp/Entities/Message/DiscordMessage.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessage.cs
@@ -635,8 +635,8 @@ internal void PopulateMentions()
if (!string.IsNullOrWhiteSpace(this.Content))
if (guild != null)
{
- this.MentionedRolesInternal = this.MentionedRolesInternal.Union(this.MentionedRoleIds.Select(xid => guild.GetRole(xid))).ToList();
- this.MentionedChannelsInternal = this.MentionedChannelsInternal.Union(Utilities.GetChannelMentions(this.Content).Select(xid => guild.GetChannel(xid))).ToList();
+ this.MentionedRolesInternal = [.. this.MentionedRolesInternal.Union(this.MentionedRoleIds.Select(guild.GetRole))!];
+ this.MentionedChannelsInternal = [.. this.MentionedChannelsInternal.Union(Utilities.GetChannelMentions(this.Content).Select(guild.GetChannel))!];
}
this.MentionedUsersInternal = [.. mentionedUsers];
@@ -651,7 +651,7 @@ internal void PopulateMentions()
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, default, this.GetMentions(), default, default, Array.Empty(), default);
+ => this.Flags?.HasMessageFlag(MessageFlags.IsComponentsV2) ?? false ? throw new InvalidOperationException("UI Kit messages can not have content.") : this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, default, this.GetMentions(), default, default, [], default);
///
/// Edits the message.
@@ -662,7 +662,7 @@ public Task ModifyAsync(Optional content)
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional embed = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, embed.Map(v => new[] { v }).ValueOr([]), this.GetMentions(), default, default, Array.Empty(), default);
+ => this.Flags?.HasMessageFlag(MessageFlags.IsComponentsV2) ?? false ? throw new InvalidOperationException("UI Kit messages can not have embeds.") : this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, default, embed.Map(v => new[] { v }).ValueOr([]), this.GetMentions(), default, default, [], default);
///
/// Edits the message.
@@ -674,7 +674,7 @@ public Task ModifyAsync(Optional embed = default)
/// Thrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content, Optional embed = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embed.Map(v => new[] { v }).ValueOr([]), this.GetMentions(), default, default, Array.Empty(), default);
+ => this.Flags?.HasMessageFlag(MessageFlags.IsComponentsV2) ?? false ? throw new InvalidOperationException("UI Kit messages can not have content or embeds.") : this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embed.Map(v => new[] { v }).ValueOr([]), this.GetMentions(), default, default, [], default);
///
/// Edits the message.
@@ -686,7 +686,7 @@ public Task ModifyAsync(Optional content, OptionalThrown when an invalid parameter was provided.
/// Thrown when Discord is unable to process the request.
public Task ModifyAsync(Optional content, Optional> embeds = default)
- => this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embeds, this.GetMentions(), default, default, Array.Empty(), default);
+ => this.Flags?.HasMessageFlag(MessageFlags.IsComponentsV2) ?? false ? throw new InvalidOperationException("UI Kit messages can not have content or embeds.") : this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, content, embeds, this.GetMentions(), default, default, [], default);
///
/// Edits the message.
@@ -699,7 +699,7 @@ public Task ModifyAsync(Optional content, Optional ModifyAsync(DiscordMessageBuilder builder)
{
builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), Optional.Some(builder.Mentions.AsEnumerable()), builder.Components, builder.EmbedsSuppressed, builder.Files, builder.Attachments.Count > 0
? Optional.Some(builder.Attachments.AsEnumerable())
: builder.KeepAttachmentsInternal.HasValue
? builder.KeepAttachmentsInternal.Value && this.Attachments is not null ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty()
@@ -737,7 +737,7 @@ public async Task ModifyAsync(Action acti
var builder = new DiscordMessageBuilder();
action(builder);
builder.Validate(true);
- return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), builder.Mentions, builder.Components, builder.Suppressed, builder.Files, builder.Attachments.Count > 0
+ return await this.Discord.ApiClient.EditMessageAsync(this.ChannelId, this.Id, builder.Content, Optional.Some(builder.Embeds.AsEnumerable()), Optional.Some(builder.Mentions.AsEnumerable()), builder.Components, builder.EmbedsSuppressed, builder.Files, builder.Attachments.Count > 0
? Optional.Some(builder.Attachments.AsEnumerable())
: builder.KeepAttachmentsInternal.HasValue
? builder.KeepAttachmentsInternal.Value && this.Attachments is not null ? Optional.Some(this.Attachments.AsEnumerable()) : Array.Empty()
@@ -983,7 +983,7 @@ private async Task> GetReactionsInternalAsync(Discord
throw new ArgumentException("Cannot get a negative number of reactions' users.");
if (limit == 0)
- return Array.Empty();
+ return [];
var users = new List(limit);
var remaining = limit;
diff --git a/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs b/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
index dc9640e599..fae2f4920c 100644
--- a/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
+++ b/DisCatSharp/Entities/Message/DiscordMessageBuilder.cs
@@ -4,65 +4,25 @@
using System.Linq;
using System.Threading.Tasks;
+using DisCatSharp.Attributes;
+using DisCatSharp.Entities.Core;
+
namespace DisCatSharp.Entities;
///
-/// Constructs a Message to be sent.
+/// Constructs a message to be sent.
///
-public sealed class DiscordMessageBuilder
+public sealed class DiscordMessageBuilder : DisCatSharpBuilder
{
- private readonly List _embeds = [];
-
- internal readonly List AttachmentsInternal = [];
-
- internal readonly List ComponentsInternal = new(5);
-
- internal readonly List FilesInternal = [];
-
- private string _content;
-
///
/// Whether to keep previous attachments.
///
internal bool? KeepAttachmentsInternal;
- ///
- /// Gets or Sets the Message to be sent.
- ///
- public string Content
- {
- get => this._content;
- set
- {
- if (value is { Length: > 2000 })
- throw new ArgumentException("Content cannot exceed 2000 characters.", nameof(value));
-
- this._content = value;
- }
- }
-
- ///
- /// Gets or sets the embed for the builder. This will always set the builder to have one embed.
- ///
- public DiscordEmbed Embed
- {
- get => this._embeds.Count > 0 ? this._embeds[0] : null;
- set
- {
- this._embeds.Clear();
- this._embeds.Add(value);
- }
- }
-
///
/// Gets the Sticker to be send.
///
- public DiscordSticker Sticker { get; set; }
-
- ///
- /// Gets the Embeds to be sent.
- ///
- public IReadOnlyList Embeds => this._embeds;
+ public DiscordSticker? Sticker { get; set; }
///
/// Gets or Sets if the message should be TTS.
@@ -74,32 +34,6 @@ public DiscordEmbed Embed
///
public bool Silent { get; private set; }
- ///
- /// Whether to send as voice message.
- /// You can't use that on your own, it needs DisCatSharp.Experimental.
- ///
- public bool IsVoiceMessage { get; private set; }
-
- ///
- /// Gets the Allowed Mentions for the message to be sent.
- ///
- public List? Mentions { get; private set; }
-
- ///
- /// Gets the Files to be sent in the Message.
- ///
- public IReadOnlyCollection Files => this.FilesInternal;
-
- ///
- /// Gets the components that will be attached to the message.
- ///
- public IReadOnlyList Components => this.ComponentsInternal;
-
- ///
- /// Gets the Attachments to be sent in the Message.
- ///
- public IReadOnlyList Attachments => this.AttachmentsInternal;
-
///
/// Gets the Reply Message ID.
///
@@ -110,11 +44,6 @@ public DiscordEmbed Embed
///
public bool MentionOnReply { get; private set; }
- ///
- /// Gets if the embeds should be suppressed.
- ///
- public bool Suppressed { get; private set; }
-
///
/// Gets if the Reply will error if the Reply Message Id does not reference a valid message.
/// If set to false, invalid replies are send as a regular message.
@@ -239,16 +168,31 @@ public DiscordMessageBuilder AddComponents(IEnumerable compone
var cmpArr = components.ToArray();
var count = cmpArr.Length;
- switch (count)
+ if (this.IsComponentsV2)
{
- case 0:
- throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
- case > 5:
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 10:
+ throw new ArgumentException("Cannot add more than 10 components!");
+ }
+
+ this.ComponentsInternal.AddRange(cmpArr);
+ }
+ else
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 5:
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
+ }
+
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this.ComponentsInternal.Add(comp);
}
-
- var comp = new DiscordActionRowComponent(cmpArr);
- this.ComponentsInternal.Add(comp);
return this;
}
@@ -269,39 +213,35 @@ public DiscordMessageBuilder HasTts(bool isTts)
///
public DiscordMessageBuilder SuppressEmbeds(bool suppress = true)
{
- this.Suppressed = suppress;
+ this.EmbedsSuppressed = suppress;
return this;
}
///
- /// Sets the message to be send as voice message.
+ /// Sets that this builder should be using UI Kit.
///
- public DiscordMessageBuilder AsVoiceMessage(bool asVoiceMessage = true)
+ /// The current builder to chain calls with.
+ public DiscordMessageBuilder WithV2Components()
{
- this.IsVoiceMessage = asVoiceMessage;
+ this.IsComponentsV2 = true;
return this;
}
///
- /// Sets the followup message to be send as silent message.
+ /// Sets the message to be send as voice message.
///
- public DiscordMessageBuilder AsSilentMessage(bool silent = true)
+ public DiscordMessageBuilder AsVoiceMessage(bool asVoiceMessage = true)
{
- this.Silent = silent;
+ this.IsVoiceMessage = asVoiceMessage;
return this;
}
///
- /// Sets the embed for the current builder.
+ /// Sets the followup message to be send as silent message.
///
- /// The embed that should be set.
- /// The current builder to be chained.
- public DiscordMessageBuilder WithEmbed(DiscordEmbed embed)
+ public DiscordMessageBuilder AsSilentMessage(bool silent = true)
{
- if (embed == null)
- return this;
-
- this.Embed = embed;
+ this.Silent = silent;
return this;
}
@@ -312,10 +252,8 @@ public DiscordMessageBuilder WithEmbed(DiscordEmbed embed)
/// The current builder to be chained.
public DiscordMessageBuilder AddEmbed(DiscordEmbed embed)
{
- if (embed == null)
- return this; //Providing null embeds will produce a 400 response from Discord.//
-
- this._embeds.Add(embed);
+ ArgumentNullException.ThrowIfNull(embed, nameof(embed));
+ this.EmbedsInternal.Add(embed);
return this;
}
@@ -326,42 +264,39 @@ public DiscordMessageBuilder AddEmbed(DiscordEmbed embed)
/// The current builder to be chained.
public DiscordMessageBuilder AddEmbeds(IEnumerable embeds)
{
- this._embeds.AddRange(embeds);
+ this.EmbedsInternal.AddRange(embeds);
return this;
}
///
/// Sets if the message has allowed mentions.
///
- /// The allowed Mention that should be sent.
+ /// The allowed Mention that should be sent.
/// The current builder to be chained.
- public DiscordMessageBuilder WithAllowedMention(IMention allowedMention)
+ public DiscordMessageBuilder WithAllowedMention(IMention mention)
{
- if (this.Mentions != null)
- this.Mentions.Add(allowedMention);
- else
- this.Mentions = [allowedMention];
-
+ this.MentionsInternal.Add(mention);
return this;
}
///
/// Sets if the message has allowed mentions.
///
- /// The allowed Mentions that should be sent.
+ /// The allowed Mentions that should be sent.
/// The current builder to be chained.
- public DiscordMessageBuilder WithAllowedMentions(IEnumerable allowedMentions)
+ public DiscordMessageBuilder WithAllowedMentions(IEnumerable mentions)
{
- if (this.Mentions != null)
- this.Mentions.AddRange(allowedMentions);
- else
- this.Mentions = allowedMentions.ToList();
-
+ this.MentionsInternal.AddRange(mentions);
return this;
}
+ ///
+ [Deprecated("Replaced by AddFile to streamline builders.")]
+ public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool resetStreamPosition = false, string description = null)
+ => this.AddFile(fileName, stream, resetStreamPosition, description);
+
///
- /// Sets if the message has files to be sent.
+ /// Adds a file to the message.
///
/// The fileName that the file should be sent as.
/// The Stream to the file.
@@ -371,9 +306,9 @@ public DiscordMessageBuilder WithAllowedMentions(IEnumerable allowedMe
///
/// Description of the file.
/// The current builder to be chained.
- public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool resetStreamPosition = false, string description = null)
+ public DiscordMessageBuilder AddFile(string fileName, Stream stream, bool resetStreamPosition = false, string? description = null)
{
- if (this.Files.Count > 10)
+ if (this.FilesInternal.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
if (this.FilesInternal.Any(x => x.Filename == fileName))
@@ -387,8 +322,13 @@ public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool reset
return this;
}
+ ///
+ [Deprecated("Replaced by AddFile to streamline builders.")]
+ public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPosition = false, string description = null)
+ => this.AddFile(stream, resetStreamPosition, description);
+
///
- /// Sets if the message has files to be sent.
+ /// Adds a file to the message.
///
/// The Stream to the file.
///
@@ -397,9 +337,9 @@ public DiscordMessageBuilder WithFile(string fileName, Stream stream, bool reset
///
/// Description of the file.
/// The current builder to be chained.
- public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPosition = false, string description = null)
+ public DiscordMessageBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string? description = null)
{
- if (this.Files.Count > 10)
+ if (this.FilesInternal.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
if (this.FilesInternal.Any(x => x.Filename == stream.Name))
@@ -413,8 +353,13 @@ public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPositio
return this;
}
+ ///
+ [Deprecated("Replaced by AddFiles to streamline builders.")]
+ public DiscordMessageBuilder WithFiles(Dictionary files, bool resetStreamPosition = false)
+ => this.AddFiles(files, resetStreamPosition);
+
///
- /// Sets if the message has files to be sent.
+ /// Adds file to the message.
///
/// The Files that should be sent.
///
@@ -422,9 +367,9 @@ public DiscordMessageBuilder WithFile(FileStream stream, bool resetStreamPositio
/// sent.
///
/// The current builder to be chained.
- public DiscordMessageBuilder WithFiles(Dictionary files, bool resetStreamPosition = false)
+ public DiscordMessageBuilder AddFiles(Dictionary files, bool resetStreamPosition = false)
{
- if (this.Files.Count + files.Count > 10)
+ if (this.FilesInternal.Count + files.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
foreach (var file in files)
@@ -476,10 +421,7 @@ public DiscordMessageBuilder WithReply(ulong messageId, bool mention = false, bo
this.FailOnInvalidReply = failOnInvalidReply;
if (mention)
- {
- this.Mentions ??= [];
- this.Mentions.Add(new RepliedUserMention());
- }
+ this.MentionsInternal.Add(new RepliedUserMention());
return this;
}
@@ -504,12 +446,6 @@ public Task SendAsync(DiscordChannel channel)
public Task ModifyAsync(DiscordMessage msg)
=> msg.ModifyAsync(this);
- ///
- /// Clears all message components on this builder.
- ///
- public void ClearComponents()
- => this.ComponentsInternal.Clear();
-
///
/// Clears the poll from this builder.
///
@@ -519,24 +455,17 @@ public void ClearPoll()
///
/// Allows for clearing the Message Builder so that it can be used again to send a new message.
///
- public void Clear()
+ public override void Clear()
{
- this.Content = "";
- this._embeds.Clear();
this.IsTts = false;
- this.Mentions = null;
- this.FilesInternal.Clear();
this.ReplyId = null;
this.MentionOnReply = false;
- this.ComponentsInternal.Clear();
- this.Suppressed = false;
- this.Sticker = null;
- this.AttachmentsInternal.Clear();
+ this.Sticker = null!;
this.KeepAttachmentsInternal = false;
this.Nonce = null;
this.EnforceNonce = false;
this.Poll = null;
- this.IsVoiceMessage = false;
+ base.Clear();
}
///
@@ -545,7 +474,7 @@ public void Clear()
/// Tells the method to perform the Modify Validation or Create Validation.
internal void Validate(bool isModify = false)
{
- if (this._embeds.Count > 10)
+ if (this.EmbedsInternal.Count > 10)
throw new ArgumentException("A message can only have up to 10 embeds.");
if (isModify)
@@ -553,14 +482,19 @@ internal void Validate(bool isModify = false)
if (!isModify)
{
- if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && (!this.Embeds?.Any() ?? true) && this.Sticker is null && (!this.Components?.Any() ?? true) && this.Poll is null && this?.Attachments.Count == 0)
+ if (this.Files?.Count == 0 && string.IsNullOrEmpty(this.Content) && !this.Embeds.Any() && this.Sticker is null && (!this.Components?.Any() ?? true) && this.Poll is null && this?.Attachments.Count == 0)
throw new ArgumentException("You must specify content, an embed, a sticker, a component, a poll or at least one file.");
- if (this.Components.Count > 5)
- throw new InvalidOperationException("You can only have 5 action rows per message.");
+ if (this.IsComponentsV2 && (!string.IsNullOrEmpty(this.Content) || this.Embeds.Any()))
+ throw new ArgumentException("Using UI Kit mode. You cannot specify content or embeds.");
- if (this.Components.Any(c => c.Components.Count > 5))
- throw new InvalidOperationException("Action rows can only have 5 components");
+ switch (this.IsComponentsV2)
+ {
+ case true when this.Components?.Count > 10:
+ throw new InvalidOperationException("You can only have 10 components per message.");
+ case false when this.Components?.Count > 5:
+ throw new InvalidOperationException("You can only have 5 action rows per message.");
+ }
if (this.EnforceNonce && string.IsNullOrEmpty(this.Nonce))
throw new InvalidOperationException("Nonce enforcement is enabled, but no nonce is set.");
@@ -569,5 +503,7 @@ internal void Validate(bool isModify = false)
}
else if (this.Poll is not null)
throw new InvalidOperationException("Messages with polls can't be edited.");
+
+ base.Validate();
}
}
diff --git a/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs b/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
index ff9f9838bd..2bceabd79e 100644
--- a/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
+++ b/DisCatSharp/Entities/Webhook/DiscordWebhookBuilder.cs
@@ -4,6 +4,7 @@
using System.Linq;
using System.Threading.Tasks;
+using DisCatSharp.Entities.Core;
using DisCatSharp.Enums;
namespace DisCatSharp.Entities;
@@ -11,25 +12,10 @@ namespace DisCatSharp.Entities;
///
/// Constructs ready-to-send webhook requests.
///
-public sealed class DiscordWebhookBuilder
+public sealed class DiscordWebhookBuilder : DisCatSharpBuilder
{
private readonly List _appliedTags = [];
- private readonly List _components = [];
-
- private readonly List _embeds = [];
-
- private readonly List _files = [];
-
- private string _content;
-
- internal readonly List AttachmentsInternal = [];
-
- ///
- /// Whether flags were changed.
- ///
- internal bool FlagsChanged = false;
-
///
/// Whether to keep previous attachments.
///
@@ -39,7 +25,7 @@ public sealed class DiscordWebhookBuilder
/// Constructs a new empty webhook request builder.
///
public DiscordWebhookBuilder()
- { } // I still see no point in initializing collections with empty collections. //
+ { }
///
/// Username to use for this webhook request.
@@ -56,68 +42,12 @@ public DiscordWebhookBuilder()
///
public bool IsTts { get; private set; }
- ///
- /// Whether to suppress embeds.
- ///
- public bool EmbedsSuppressed { get; private set; }
-
- ///
- /// Whether to send as silent message.
- ///
- public bool NotificationsSuppressed { get; private set; }
-
- ///
- /// Whether to send as voice message.
- /// You can't use that on your own, it needs DisCatSharp.Experimental.
- ///
- public bool IsVoiceMessage { get; private set; }
-
- ///
- /// Message to send on this webhook request.
- ///
- public string Content
- {
- get => this._content;
- set
- {
- if (value is { Length: > 2000 })
- throw new ArgumentException("Content length cannot exceed 2000 characters.", nameof(value));
-
- this._content = value;
- }
- }
-
///
/// Name of the new thread.
/// Only works if the webhook is send in a .
///
public string? ThreadName { get; set; }
- ///
- /// Embeds to send on this webhook request.
- ///
- public IReadOnlyList Embeds => this._embeds;
-
- ///
- /// Files to send on this webhook request.
- ///
- public IReadOnlyList Files => this._files;
-
- ///
- /// Mentions to send on this webhook request.
- ///
- public List? Mentions { get; private set; }
-
- ///
- /// Gets the components.
- ///
- public IReadOnlyList Components => this._components;
-
- ///
- /// Attachments to keep on this webhook request.
- ///
- public IReadOnlyList Attachments => this.AttachmentsInternal;
-
///
/// Forum post tags to send on this webhook request.
///
@@ -133,7 +63,6 @@ public string Content
///
public DiscordWebhookBuilder SuppressEmbeds()
{
- this.FlagsChanged = true;
this.EmbedsSuppressed = true;
return this;
}
@@ -143,7 +72,6 @@ public DiscordWebhookBuilder SuppressEmbeds()
///
public DiscordWebhookBuilder AsSilentMessage()
{
- this.FlagsChanged = true;
this.NotificationsSuppressed = true;
return this;
}
@@ -153,7 +81,6 @@ public DiscordWebhookBuilder AsSilentMessage()
///
public DiscordWebhookBuilder AsVoiceMessage()
{
- this.FlagsChanged = true;
this.IsVoiceMessage = true;
return this;
}
@@ -176,11 +103,11 @@ public DiscordWebhookBuilder AddComponents(IEnumerable 5)
+ if (ara.Length + this.ComponentsInternal.Count > 5)
throw new ArgumentException("ActionRow count exceeds maximum of five.");
foreach (var ar in ara)
- this._components.Add(ar);
+ this.ComponentsInternal.Add(ar);
return this;
}
@@ -196,17 +123,43 @@ public DiscordWebhookBuilder AddComponents(IEnumerable compone
var cmpArr = components.ToArray();
var count = cmpArr.Length;
- switch (count)
+ if (this.IsComponentsV2)
+ {
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 10:
+ throw new ArgumentException("Cannot add more than 10 components!");
+ }
+
+ this.ComponentsInternal.AddRange(cmpArr);
+ }
+ else
{
- case 0:
- throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
- case > 5:
- throw new ArgumentException("Cannot add more than 5 components per action row!");
+ switch (count)
+ {
+ case 0:
+ throw new ArgumentOutOfRangeException(nameof(components), "You must provide at least one component");
+ case > 5:
+ throw new ArgumentException("Cannot add more than 5 components per action row!");
+ }
+
+ var comp = new DiscordActionRowComponent(cmpArr);
+ this.ComponentsInternal.Add(comp);
}
- var comp = new DiscordActionRowComponent(cmpArr);
- this._components.Add(comp);
+ return this;
+ }
+ ///
+ /// Sets that this builder should be using UI Kit.
+ ///
+ /// The current builder to chain calls with.
+ public DiscordWebhookBuilder WithV2Components()
+ {
+ this.FlagsChanged = true;
+ this.IsComponentsV2 = true;
return this;
}
@@ -278,9 +231,8 @@ public DiscordWebhookBuilder WithThreadName(string name)
/// Embed to add.
public DiscordWebhookBuilder AddEmbed(DiscordEmbed embed)
{
- if (embed != null)
- this._embeds.Add(embed);
-
+ ArgumentNullException.ThrowIfNull(embed, nameof(embed));
+ this.EmbedsInternal.Add(embed);
return this;
}
@@ -290,7 +242,7 @@ public DiscordWebhookBuilder AddEmbed(DiscordEmbed embed)
/// Embeds to add.
public DiscordWebhookBuilder AddEmbeds(IEnumerable embeds)
{
- this._embeds.AddRange(embeds);
+ this.EmbedsInternal.AddRange(embeds);
return this;
}
@@ -306,16 +258,16 @@ public DiscordWebhookBuilder AddEmbeds(IEnumerable embeds)
/// Description of the file.
public DiscordWebhookBuilder AddFile(string filename, Stream data, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count > 10)
+ if (this.FilesInternal.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == filename))
+ if (this.FilesInternal.Any(x => x.Filename == filename))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(filename, data, data.Position, description: description));
+ this.FilesInternal.Add(new(filename, data, data.Position, description: description));
else
- this._files.Add(new(filename, data, null, description: description));
+ this.FilesInternal.Add(new(filename, data, null, description: description));
return this;
}
@@ -332,16 +284,16 @@ public DiscordWebhookBuilder AddFile(string filename, Stream data, bool resetStr
///
public DiscordWebhookBuilder AddFile(FileStream stream, bool resetStreamPosition = false, string description = null)
{
- if (this.Files.Count > 10)
+ if (this.FilesInternal.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
- if (this._files.Any(x => x.Filename == stream.Name))
+ if (this.FilesInternal.Any(x => x.Filename == stream.Name))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(stream.Name, stream, stream.Position, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, stream.Position, description: description));
else
- this._files.Add(new(stream.Name, stream, null, description: description));
+ this.FilesInternal.Add(new(stream.Name, stream, null, description: description));
return this;
}
@@ -356,18 +308,18 @@ public DiscordWebhookBuilder AddFile(FileStream stream, bool resetStreamPosition
///
public DiscordWebhookBuilder AddFiles(Dictionary files, bool resetStreamPosition = false)
{
- if (this.Files.Count + files.Count > 10)
+ if (this.FilesInternal.Count + files.Count > 10)
throw new ArgumentException("Cannot send more than 10 files with a single message.");
foreach (var file in files)
{
- if (this._files.Any(x => x.Filename == file.Key))
+ if (this.FilesInternal.Any(x => x.Filename == file.Key))
throw new ArgumentException("A File with that filename already exists");
if (resetStreamPosition)
- this._files.Add(new(file.Key, file.Value, file.Value.Position));
+ this.FilesInternal.Add(new(file.Key, file.Value, file.Value.Position));
else
- this._files.Add(new(file.Key, file.Value, null));
+ this.FilesInternal.Add(new(file.Key, file.Value, null));
}
return this;
@@ -397,26 +349,20 @@ public DiscordWebhookBuilder KeepAttachments(bool keep)
///
/// Adds the mention to the mentions to parse, etc. at the execution of the webhook.
///
- /// Mention to add.
- public DiscordWebhookBuilder WithAllowedMention(IMention allowedMention)
+ /// Mention to add.
+ public DiscordWebhookBuilder WithAllowedMention(IMention mention)
{
- if (this.Mentions != null)
- this.Mentions.Add(allowedMention);
- else
- this.Mentions = [allowedMention];
+ this.MentionsInternal.Add(mention);
return this;
}
///
/// Adds the mentions to the mentions to parse, etc. at the execution of the webhook.
///
- /// Mentions to add.
- public DiscordWebhookBuilder WithAllowedMentions(IEnumerable allowedMentions)
+ /// Mentions to add.
+ public DiscordWebhookBuilder WithAllowedMentions(IEnumerable mentions)
{
- if (this.Mentions != null)
- this.Mentions.AddRange(allowedMentions);
- else
- this.Mentions = allowedMentions.ToList();
+ this.MentionsInternal.AddRange(mentions);
return this;
}
@@ -435,7 +381,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
///
/// The webhook that should be executed.
/// The message sent
- public async Task SendAsync(DiscordWebhook webhook) => await webhook.ExecuteAsync(this).ConfigureAwait(false);
+ public async Task SendAsync(DiscordWebhook webhook)
+ => await webhook.ExecuteAsync(this).ConfigureAwait(false);
///
/// Executes a webhook.
@@ -443,7 +390,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
/// The webhook that should be executed.
/// Target thread id.
/// The message sent
- public async Task SendAsync(DiscordWebhook webhook, ulong threadId) => await webhook.ExecuteAsync(this, threadId.ToString()).ConfigureAwait(false);
+ public async Task SendAsync(DiscordWebhook webhook, ulong threadId)
+ => await webhook.ExecuteAsync(this, threadId.ToString()).ConfigureAwait(false);
///
/// Sends the modified webhook message.
@@ -451,7 +399,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
/// The webhook that should be executed.
/// The message to modify.
/// The modified message
- public async Task ModifyAsync(DiscordWebhook webhook, DiscordMessage message) => await this.ModifyAsync(webhook, message.Id).ConfigureAwait(false);
+ public async Task ModifyAsync(DiscordWebhook webhook, DiscordMessage message)
+ => await this.ModifyAsync(webhook, message.Id).ConfigureAwait(false);
///
/// Sends the modified webhook message.
@@ -459,7 +408,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
/// The webhook that should be executed.
/// The id of the message to modify.
/// The modified message
- public async Task ModifyAsync(DiscordWebhook webhook, ulong messageId) => await webhook.EditMessageAsync(messageId, this).ConfigureAwait(false);
+ public async Task ModifyAsync(DiscordWebhook webhook, ulong messageId)
+ => await webhook.EditMessageAsync(messageId, this).ConfigureAwait(false);
///
/// Sends the modified webhook message.
@@ -468,7 +418,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
/// The message to modify.
/// Target thread.
/// The modified message
- public async Task ModifyAsync(DiscordWebhook webhook, DiscordMessage message, DiscordThreadChannel thread) => await this.ModifyAsync(webhook, message.Id, thread.Id).ConfigureAwait(false);
+ public async Task ModifyAsync(DiscordWebhook webhook, DiscordMessage message, DiscordThreadChannel thread)
+ => await this.ModifyAsync(webhook, message.Id, thread.Id).ConfigureAwait(false);
///
/// Sends the modified webhook message.
@@ -477,13 +428,8 @@ public DiscordWebhookBuilder WithAppliedTags(IEnumerable tags)
/// The id of the message to modify.
/// Target thread id.
/// The modified message
- public async Task ModifyAsync(DiscordWebhook webhook, ulong messageId, ulong threadId) => await webhook.EditMessageAsync(messageId, this, threadId.ToString()).ConfigureAwait(false);
-
- ///
- /// Clears all message components on this builder.
- ///
- public void ClearComponents()
- => this._components.Clear();
+ public async Task ModifyAsync(DiscordWebhook webhook, ulong messageId, ulong threadId)
+ => await webhook.EditMessageAsync(messageId, this, threadId.ToString()).ConfigureAwait(false);
///
/// Clears the poll from this builder.
@@ -494,22 +440,13 @@ public void ClearPoll()
///
/// Allows for clearing the Webhook Builder so that it can be used again to send a new message.
///
- public void Clear()
+ public override void Clear()
{
- this.Content = "";
- this._embeds.Clear();
this.IsTts = false;
- this.Mentions = null;
- this._files.Clear();
- this.AttachmentsInternal.Clear();
- this._components.Clear();
this.KeepAttachmentsInternal = false;
this.ThreadName = null;
this.Poll = null;
- this.FlagsChanged = false;
- this.NotificationsSuppressed = false;
- this.IsTts = false;
- this.IsVoiceMessage = false;
+ base.Clear();
}
///
@@ -555,5 +492,7 @@ internal void Validate(bool isModify = false, bool isFollowup = false, bool isIn
throw new ArgumentException("You must specify content, an embed, a component, a poll, or at least one file.");
this.Poll?.Validate();
+
+ base.Validate();
}
}
diff --git a/DisCatSharp/Enums/Interaction/ComponentType.cs b/DisCatSharp/Enums/Interaction/ComponentType.cs
index c1ecfc4c33..12864c84c4 100644
--- a/DisCatSharp/Enums/Interaction/ComponentType.cs
+++ b/DisCatSharp/Enums/Interaction/ComponentType.cs
@@ -45,8 +45,43 @@ public enum ComponentType
///
ChannelSelect = 8,
+ ///
+ /// A section.
+ ///
+ Section = 9,
+
+ ///
+ /// A text display.
+ ///
+ TextDisplay = 10,
+
+ ///
+ /// A thumbnail.
+ ///
+ Thumbnail = 11,
+
+ ///
+ /// A media gallery.
+ ///
+ MediaGallery = 12,
+
///
/// A file.
///
- File = 13
+ File = 13,
+
+ ///
+ /// A separator.
+ ///
+ Separator = 14,
+
+ ///
+ /// Cannot be used by bots.
+ ///
+ ContentInventoryEntry = 15,
+
+ ///
+ /// A container.
+ ///
+ Container = 17
}
diff --git a/DisCatSharp/Enums/Interaction/SeparatorSpacingSize.cs b/DisCatSharp/Enums/Interaction/SeparatorSpacingSize.cs
new file mode 100644
index 0000000000..b279af4885
--- /dev/null
+++ b/DisCatSharp/Enums/Interaction/SeparatorSpacingSize.cs
@@ -0,0 +1,17 @@
+namespace DisCatSharp.Enums;
+
+///
+/// Represents the sperator spacing size of a .
+///
+public enum SeparatorSpacingSize
+{
+ ///
+ /// A small spacing size.
+ ///
+ Small = 1,
+
+ ///
+ /// A large spacing size.
+ ///
+ Large = 2
+}
diff --git a/DisCatSharp/Enums/Message/MessageFlags.cs b/DisCatSharp/Enums/Message/MessageFlags.cs
index 2ea21e97e7..4bc53b490c 100644
--- a/DisCatSharp/Enums/Message/MessageFlags.cs
+++ b/DisCatSharp/Enums/Message/MessageFlags.cs
@@ -86,5 +86,10 @@ public enum MessageFlags
///
/// The message is a voice message.
///
- IsVoiceMessage = 1 << 13
+ IsVoiceMessage = 1 << 13,
+
+ ///
+ /// The message uses the UI Kit.
+ ///
+ IsComponentsV2 = 1 << 15
}
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
index b667cc1a98..b009199de6 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestApplicationCommandPayloads.cs
@@ -253,7 +253,7 @@ internal sealed class RestFollowupMessageCreatePayload : ObservableApiObject
/// Gets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public DiscordMentions Mentions { get; set; }
+ public DiscordMentions? Mentions { get; set; }
///
/// Gets the flags.
@@ -265,7 +265,7 @@ internal sealed class RestFollowupMessageCreatePayload : ObservableApiObject
/// Gets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection Components { get; set; }
+ public IReadOnlyCollection Components { get; set; }
///
/// Gets attachments.
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
index a890c6c672..64706c5292 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestChannelPayloads.cs
@@ -280,7 +280,7 @@ internal class RestChannelMessageEditPayload : ObservableApiObject
/// Gets or sets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IReadOnlyCollection Components { get; set; }
+ public IReadOnlyCollection Components { get; set; }
///
/// Gets or sets a value indicating whether has embed.
diff --git a/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs b/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
index 7caedb5cf4..f4cfeeb14d 100644
--- a/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
+++ b/DisCatSharp/Net/Abstractions/Rest/RestWebhookPayloads.cs
@@ -88,7 +88,7 @@ internal sealed class RestWebhookExecutePayload : ObservableApiObject
/// Gets or sets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable Components { get; set; }
+ public IEnumerable Components { get; set; }
///
/// Gets or sets the attachments.
@@ -136,7 +136,7 @@ internal sealed class RestWebhookMessageEditPayload : ObservableApiObject
/// Gets or sets the mentions.
///
[JsonProperty("allowed_mentions", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable Mentions { get; set; }
+ public DiscordMentions? Mentions { get; set; }
///
/// Gets or sets the attachments.
@@ -148,7 +148,7 @@ internal sealed class RestWebhookMessageEditPayload : ObservableApiObject
/// Gets or sets the components.
///
[JsonProperty("components", NullValueHandling = NullValueHandling.Ignore)]
- public IEnumerable Components { get; set; }
+ public IEnumerable Components { get; set; }
[JsonProperty("flags", NullValueHandling = NullValueHandling.Ignore)]
public MessageFlags? Flags { get; set; }
diff --git a/DisCatSharp/Net/Rest/DiscordApiClient.cs b/DisCatSharp/Net/Rest/DiscordApiClient.cs
index 140a7cb3e4..ff249d0758 100644
--- a/DisCatSharp/Net/Rest/DiscordApiClient.cs
+++ b/DisCatSharp/Net/Rest/DiscordApiClient.cs
@@ -105,7 +105,7 @@ internal DiscordApiClient(IWebProxy proxy, TimeSpan timeout, bool useRelativeRat
///
/// The values.
/// Whether this query will be transmitted via POST.
- private static string BuildQueryString(IDictionary values, bool post = false)
+ private static string BuildQueryString(Dictionary values, bool post = false)
{
if (values == null || values.Count == 0)
return string.Empty;
@@ -130,7 +130,7 @@ private DiscordMessage PrepareMessage(JToken msgRaw)
this.PopulateMessage(author, ret);
var referencedMsg = msgRaw["referenced_message"];
- if (ret is { InternalReference: { Type: ReferenceType.Default }, MessageType: MessageType.Reply } && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
+ if (ret is { InternalReference.Type: ReferenceType.Default, MessageType: MessageType.Reply } && !string.IsNullOrWhiteSpace(referencedMsg?.ToString()))
{
author = referencedMsg["author"].ToObject();
ret.ReferencedMessage.Discord = this.Discord;
@@ -1107,7 +1107,7 @@ internal async Task> GetGuildBansAsync(ulong guildId,
xb.User = usr;
return xb;
});
- var bans = new ReadOnlyCollection(new List(bansRaw));
+ var bans = new ReadOnlyCollection([.. bansRaw]);
return bans;
}
@@ -1160,7 +1160,7 @@ internal async Task CreateGuildBulkBanAsync(ulong guildI
var pld = new RestGuildBulkBanPayload
{
- UserIds = userIds.ToList(),
+ UserIds = [.. userIds],
DeleteMessageSeconds = deleteMessageSeconds
};
@@ -1501,18 +1501,18 @@ internal async Task GetGuildWidgetAsync(ulong guildId)
ret.Guild = this.Discord.Guilds.ContainsKey(guildId) ? this.Discord.Guilds[guildId] : null;
ret.Channels = ret.Guild == null
- ? rawChannels.Select(r => new DiscordChannel
+ ? [.. rawChannels.Select(r => new DiscordChannel
{
Id = (ulong)r["id"],
Name = r["name"].ToString(),
Position = (int)r["position"]
- }).ToList()
- : rawChannels.Select(r =>
+ })]
+ : [.. rawChannels.Select(r =>
{
var c = ret.Guild.GetChannel((ulong)r["id"]);
c.Position = (int)r["position"];
return c;
- }).ToList();
+ })];
return ret;
}
@@ -1589,7 +1589,7 @@ internal async Task> GetGuildTemplatesAsync(
var templatesRaw = JsonConvert.DeserializeObject>(res.Response);
- return new ReadOnlyCollection(new List(templatesRaw));
+ return new ReadOnlyCollection([.. templatesRaw]);
}
///
@@ -3039,7 +3039,7 @@ internal async Task GetMessageAsync(ulong channelId, ulong messa
/// Thrown when the exceeds 2000 characters or is empty and
/// if neither content, sticker, components and embeds are definied..
///
- internal async Task CreateMessageAsync(ulong channelId, string content, IEnumerable embeds, DiscordSticker sticker, ulong? replyMessageId, bool mentionReply, bool failOnInvalidReply, ReadOnlyCollection? components = null)
+ internal async Task CreateMessageAsync(ulong channelId, string content, IEnumerable embeds, DiscordSticker sticker, ulong? replyMessageId, bool mentionReply, bool failOnInvalidReply, ReadOnlyCollection? components = null)
{
if (content is { Length: > 2000 })
throw new ArgumentException("Message content length cannot exceed 2000 characters.");
@@ -3110,12 +3110,14 @@ internal async Task CreateMessageAsync(ulong channelId, DiscordM
embed.Timestamp = embed.Timestamp.Value.ToUniversalTime();
var flags = MessageFlags.None;
- if (builder.Suppressed)
+ if (builder.EmbedsSuppressed)
flags |= MessageFlags.SuppressedEmbeds;
if (builder.Silent)
flags |= MessageFlags.SuppressNotifications;
if (builder.IsVoiceMessage)
flags |= MessageFlags.IsVoiceMessage;
+ if (builder.IsComponentsV2)
+ flags |= MessageFlags.IsComponentsV2;
var pld = new RestChannelMessageCreatePayload
{
@@ -3142,7 +3144,7 @@ internal async Task CreateMessageAsync(ulong channelId, DiscordM
FailIfNotExists = builder.FailOnInvalidReply
};
- pld.Mentions = new(builder.Mentions ?? Mentions.All, builder.Mentions?.Any() ?? false, builder.MentionOnReply);
+ pld.Mentions = new(builder.Mentions.Count == 0 ? Mentions.All : builder.Mentions, builder.Mentions.Any(), builder.MentionOnReply);
if (builder.Files.Count == 0)
{
@@ -3277,7 +3279,7 @@ internal async Task> GetGuildChannelsAsync(ulong g
foreach (var ret in channelsRaw)
ret.Initialize(this.Discord);
- return new ReadOnlyCollection(new List(channelsRaw));
+ return new ReadOnlyCollection([.. channelsRaw]);
}
///
@@ -3435,7 +3437,7 @@ internal async Task> GetChannelMessagesAsync(ulong
foreach (var xj in msgsRaw)
msgs.Add(this.PrepareMessage(xj));
- return new ReadOnlyCollection(new List(msgs));
+ return new ReadOnlyCollection([.. msgs]);
}
///
@@ -3472,7 +3474,7 @@ internal async Task GetChannelMessageAsync(ulong channelId, ulon
/// The suppress_embed.
/// The files.
/// The attachments to keep.
- internal async Task EditMessageAsync(ulong channelId, ulong messageId, Optional content, Optional> embeds, Optional> mentions, IReadOnlyList components, Optional suppressEmbed, IReadOnlyCollection files, Optional> attachments)
+ internal async Task EditMessageAsync(ulong channelId, ulong messageId, Optional content, Optional> embeds, Optional> mentions, IReadOnlyList components, Optional suppressEmbed, IReadOnlyCollection files, Optional> attachments)
{
if (embeds is { HasValue: true, Value: not null })
foreach (var embed in embeds.Value)
@@ -3675,7 +3677,7 @@ internal async Task> GetAnswerVotersAsync(ulong
voters.Add(usr);
}
- return new(new List(voters));
+ return new([.. voters]);
}
///
@@ -3721,7 +3723,7 @@ internal async Task> GetChannelInvitesAsync(ulong c
return xi;
});
- return new ReadOnlyCollection(new List(invitesRaw));
+ return new ReadOnlyCollection([.. invitesRaw]);
}
///
@@ -3860,7 +3862,7 @@ internal async Task> GetPinnedMessagesAsync(ulong
foreach (var xj in msgsRaw)
msgs.Add(this.PrepareMessage(xj));
- return new ReadOnlyCollection(new List(msgs));
+ return new ReadOnlyCollection([.. msgs]);
}
///
@@ -4210,7 +4212,7 @@ internal async Task> GetCurrentUserGuildsAsync(int l
{
var guildsRaw = DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
var glds = guildsRaw.Select(xug => (this.Discord as DiscordClient)?.GuildsInternal[xug.Id]);
- return new ReadOnlyCollection(new List(glds));
+ return new ReadOnlyCollection([.. glds]);
}
return DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
@@ -4978,7 +4980,7 @@ internal async Task> GetChannelWebhooksAsync(ulong
xw.ApiClient = this;
return xw;
});
- return webhooksRaw.ToList();
+ return [.. webhooksRaw];
}
///
@@ -5001,7 +5003,7 @@ internal async Task> GetGuildWebhooksAsync(ulong g
xw.ApiClient = this;
return xw;
});
- return webhooksRaw.ToList();
+ return [.. webhooksRaw];
}
///
@@ -5207,7 +5209,7 @@ internal async Task ExecuteWebhookAsync(ulong webhookId, string
DiscordPollRequest = builder.Poll?.Build()
};
- if (builder.Mentions != null)
+ if (builder.Mentions.Any())
pld.Mentions = new(builder.Mentions, builder.Mentions.Count is not 0);
if (builder.Files?.Count > 0)
@@ -5247,7 +5249,7 @@ internal async Task ExecuteWebhookAsync(ulong webhookId, string
}
}
- if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.Files?.Count > 0 || builder.IsTts || builder.Mentions != null)
+ if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.Files?.Count > 0 || builder.IsTts || builder.Mentions.Any())
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.WEBHOOKS}/:webhook_id/:webhook_token";
@@ -5342,16 +5344,20 @@ internal async Task EditWebhookMessageAsync(ulong webhookId, str
flags |= MessageFlags.SuppressedEmbeds;
if (builder.NotificationsSuppressed)
flags |= MessageFlags.SuppressNotifications;
+ if (builder.IsComponentsV2)
+ flags |= MessageFlags.IsComponentsV2;
var pld = new RestWebhookMessageEditPayload
{
Content = builder.Content,
Embeds = builder.Embeds,
- Mentions = builder.Mentions,
Components = builder.Components,
Flags = flags
};
+ if (builder.Mentions.Any())
+ pld.Mentions = new(builder.Mentions, builder.Mentions.Count is not 0);
+
if (builder.Files?.Count > 0)
{
ulong fileId = 0;
@@ -5662,7 +5668,7 @@ internal async Task> GetReactionsAsync(ulong channelI
reacters.Add(usr);
}
- return new ReadOnlyCollection(new List(reacters));
+ return new ReadOnlyCollection([.. reacters]);
}
///
@@ -6321,7 +6327,7 @@ internal async Task> GetApplicationEmojis
var emojisRaw = JsonConvert.DeserializeObject(res.Response);
- return this.Discord.UpdateCachedApplicationEmojis(emojisRaw?.Value("items")).Select(x => x.Value).ToList();
+ return [.. this.Discord.UpdateCachedApplicationEmojis(emojisRaw?.Value("items")).Select(x => x.Value)];
}
///
@@ -6490,7 +6496,7 @@ internal async Task> GetStickerPacksAsync()
var json = JObject.Parse(res.Response)["sticker_packs"] as JArray;
var ret = json.ToDiscordObject();
- return ret.ToList();
+ return [.. ret];
}
///
@@ -6647,7 +6653,7 @@ internal async Task> GetGlobalApplicati
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
- return ret.ToList();
+ return [.. ret];
}
///
@@ -6684,7 +6690,7 @@ internal async Task> BulkOverwriteGloba
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
- return ret.ToList();
+ return [.. ret];
}
///
@@ -6848,7 +6854,7 @@ internal async Task> GetGuildApplicatio
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.GET, route).ConfigureAwait(false);
var ret = DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
- return ret.ToList();
+ return [.. ret];
}
///
@@ -6886,7 +6892,7 @@ internal async Task> BulkOverwriteGuild
var res = await this.DoRequestAsync(this.Discord, bucket, url, RestRequestMethod.PUT, route, payload: DiscordJson.SerializeObject(pld)).ConfigureAwait(false);
var ret = DiscordJson.DeserializeIEnumerableObject>(res.Response, this.Discord);
- return ret.ToList();
+ return [.. ret];
}
///
@@ -7062,6 +7068,8 @@ internal async Task CreateInteractionRespons
flags |= MessageFlags.SuppressedEmbeds;
if (builder.NotificationsSuppressed)
flags |= MessageFlags.SuppressNotifications;
+ if (builder.IsComponentsV2)
+ flags |= MessageFlags.IsComponentsV2;
}
var data = builder is not null
@@ -7070,7 +7078,7 @@ internal async Task CreateInteractionRespons
Content = builder?.Content ?? null,
Embeds = builder?.Embeds ?? null,
IsTts = builder?.IsTts,
- Mentions = builder?.Mentions ?? null,
+ Mentions = (builder?.Mentions.Any() ?? false) ? new(builder.Mentions, builder.Mentions.Count is not 0) : null,
Flags = flags,
Components = builder?.Components ?? null,
Choices = null,
@@ -7143,7 +7151,7 @@ internal async Task CreateInteractionRespons
var values = new Dictionary();
if (builder is not null)
- if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts || builder.Mentions is not null || builder.Files?.Count > 0 || builder.Components?.Count > 0)
+ if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts || builder.Mentions.Any() || builder.Files?.Count > 0 || builder.Components?.Count > 0)
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.INTERACTIONS}/:interaction_id/:interaction_token{Endpoints.CALLBACK}";
@@ -7296,6 +7304,8 @@ internal async Task CreateFollowupMessageAsync(ulong application
flags |= MessageFlags.SuppressedEmbeds;
if (builder.NotificationsSuppressed)
flags |= MessageFlags.SuppressNotifications;
+ if (builder.IsComponentsV2)
+ flags |= MessageFlags.IsComponentsV2;
var values = new Dictionary();
var pld = new RestFollowupMessageCreatePayload
@@ -7345,10 +7355,10 @@ internal async Task CreateFollowupMessageAsync(ulong application
}
}
- if (builder.Mentions != null)
+ if (builder.Mentions.Any())
pld.Mentions = new(builder.Mentions, builder.Mentions.Count is not 0);
- if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts || builder.Mentions != null || builder.Files?.Count > 0 || builder.Components?.Count > 0)
+ if (!string.IsNullOrEmpty(builder.Content) || builder.Embeds?.Count > 0 || builder.IsTts || builder.Mentions.Any() || builder.Files?.Count > 0 || builder.Components?.Count > 0)
values["payload_json"] = DiscordJson.SerializeObject(pld);
var route = $"{Endpoints.WEBHOOKS}/:application_id/:interaction_token";
@@ -7669,7 +7679,7 @@ internal async Task> GetApplicationAssets
asset.Application = application;
}
- return new ReadOnlyCollection(new List(assets));
+ return new ReadOnlyCollection([.. assets]);
}
///
diff --git a/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs b/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
index f1e57fe39e..9d1fb23d91 100644
--- a/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
+++ b/DisCatSharp/Net/Serialization/DiscordComponentJsonConverter.cs
@@ -54,6 +54,12 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer
ComponentType.RoleSelect => new DiscordRoleSelectComponent(),
ComponentType.MentionableSelect => new DiscordMentionableSelectComponent(),
ComponentType.ChannelSelect => new DiscordChannelSelectComponent(),
+ ComponentType.Section => new DiscordSectionComponent(),
+ ComponentType.TextDisplay => new DiscordTextDisplayComponent(),
+ ComponentType.Thumbnail => new DiscordThumnailComponent(),
+ ComponentType.MediaGallery => new DiscordMediaGalleryComponent(),
+ ComponentType.File => new DiscordFileDisplayComponent(),
+ ComponentType.Separator => new DiscordSeparatorComponent(),
_ => new()
{
Type = type