diff --git a/docs/guides/concepts/samples/events.cs b/docs/guides/concepts/samples/events.cs
index dce625b33..29542ef2f 100644
--- a/docs/guides/concepts/samples/events.cs
+++ b/docs/guides/concepts/samples/events.cs
@@ -22,7 +22,7 @@ public class Program
{
Console.WriteLine("Bot is connected!");
return Task.CompletedTask;
- }
+ };
await Task.Delay(-1);
}
diff --git a/docs/guides/v2_v3_guide/v2_to_v3_guide.md b/docs/guides/v2_v3_guide/v2_to_v3_guide.md
index a837f44d2..91fc1b43d 100644
--- a/docs/guides/v2_v3_guide/v2_to_v3_guide.md
+++ b/docs/guides/v2_v3_guide/v2_to_v3_guide.md
@@ -37,6 +37,7 @@ _client = new DiscordSocketClient(config);
- AllUnprivileged: This is a group of most common intents, that do NOT require any [developer portal] intents to be enabled.
This includes intents that receive messages such as: `GatewayIntents.GuildMessages, GatewayIntents.DirectMessages`
- GuildMembers: An intent disabled by default, as you need to enable it in the [developer portal].
+- MessageContent: An intent also disabled by default as you also need to enable it in the [developer portal].
- GuildPresences: Also disabled by default, this intent together with `GuildMembers` are the only intents not included in `AllUnprivileged`.
- All: All intents, it is ill advised to use this without care, as it _can_ cause a memory leak from presence.
The library will give responsive warnings if you specify unnecessary intents.
diff --git a/samples/BasicBot/Program.cs b/samples/BasicBot/Program.cs
index 179dfce05..a71de9fc8 100644
--- a/samples/BasicBot/Program.cs
+++ b/samples/BasicBot/Program.cs
@@ -34,9 +34,16 @@ namespace BasicBot
public Program()
{
+ // Config used by DiscordSocketClient
+ // Define intents for the client
+ var config = new DiscordSocketConfig
+ {
+ GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
+ };
+
// It is recommended to Dispose of a client when you are finished
// using it, at the end of your app's lifetime.
- _client = new DiscordSocketClient();
+ _client = new DiscordSocketClient(config);
// Subscribing to client events, so that we may receive them whenever they're invoked.
_client.Log += LogAsync;
diff --git a/samples/BasicBot/_BasicBot.csproj b/samples/BasicBot/_BasicBot.csproj
index e6245d340..7d3004ad9 100644
--- a/samples/BasicBot/_BasicBot.csproj
+++ b/samples/BasicBot/_BasicBot.csproj
@@ -1,4 +1,4 @@
-
+Exe
@@ -6,7 +6,7 @@
-
+
diff --git a/samples/InteractionFramework/_InteractionFramework.csproj b/samples/InteractionFramework/_InteractionFramework.csproj
index 8892a65b7..a0fa14d74 100644
--- a/samples/InteractionFramework/_InteractionFramework.csproj
+++ b/samples/InteractionFramework/_InteractionFramework.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/samples/ShardedClient/Program.cs b/samples/ShardedClient/Program.cs
index 2b8f49edb..cb7b0dbb3 100644
--- a/samples/ShardedClient/Program.cs
+++ b/samples/ShardedClient/Program.cs
@@ -28,7 +28,8 @@ namespace ShardedClient
// have 1 shard per 1500-2000 guilds your bot is in.
var config = new DiscordSocketConfig
{
- TotalShards = 2
+ TotalShards = 2,
+ GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
};
// You should dispose a service provider created using ASP.NET
diff --git a/samples/ShardedClient/_ShardedClient.csproj b/samples/ShardedClient/_ShardedClient.csproj
index 68a43c7cd..5c1c6a20c 100644
--- a/samples/ShardedClient/_ShardedClient.csproj
+++ b/samples/ShardedClient/_ShardedClient.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/samples/TextCommandFramework/Program.cs b/samples/TextCommandFramework/Program.cs
index 8a18daf72..ccd23436e 100644
--- a/samples/TextCommandFramework/Program.cs
+++ b/samples/TextCommandFramework/Program.cs
@@ -60,6 +60,10 @@ namespace TextCommandFramework
private ServiceProvider ConfigureServices()
{
return new ServiceCollection()
+ .AddSingleton(new DiscordSocketConfig
+ {
+ GatewayIntents = GatewayIntents.AllUnprivileged | GatewayIntents.MessageContent
+ })
.AddSingleton()
.AddSingleton()
.AddSingleton()
diff --git a/samples/TextCommandFramework/_TextCommandFramework.csproj b/samples/TextCommandFramework/_TextCommandFramework.csproj
index 6e00625e8..5307303ce 100644
--- a/samples/TextCommandFramework/_TextCommandFramework.csproj
+++ b/samples/TextCommandFramework/_TextCommandFramework.csproj
@@ -8,8 +8,8 @@
-
-
+
+
diff --git a/samples/WebhookClient/_WebhookClient.csproj b/samples/WebhookClient/_WebhookClient.csproj
index 515fcf3a4..acea75d2c 100644
--- a/samples/WebhookClient/_WebhookClient.csproj
+++ b/samples/WebhookClient/_WebhookClient.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Discord.Net.Core/DiscordErrorCode.cs b/src/Discord.Net.Core/DiscordErrorCode.cs
index 60b7d20d8..ccd547e92 100644
--- a/src/Discord.Net.Core/DiscordErrorCode.cs
+++ b/src/Discord.Net.Core/DiscordErrorCode.cs
@@ -94,10 +94,10 @@ namespace Discord
MaxNumberOfDailyApplicationCommandCreatesHasBeenReached = 30034,
MaximumBansForNonGuildMembersReached = 30035,
MaximumBanFetchesReached = 30037,
- MaximumUncompleteGuildScheduledEvents = 30038,
+ MaximumUncompletedGuildScheduledEvents = 30038,
MaximumStickersReached = 30039,
MaximumPruneRequestReached = 30040,
- MaximumGuildWigitsReached = 30042,
+ MaximumGuildWidgetsReached = 30042,
#endregion
#region General Request Errors (40XXX)
@@ -116,24 +116,24 @@ namespace Discord
TargetUserNotInVoice = 40032,
MessageAlreadyCrossposted = 40033,
ApplicationNameAlreadyExists = 40041,
- #endregion
-
- #region Action Preconditions/Checks (50XXX)
ApplicationInteractionFailedToSend = 40043,
CannotSendAMessageInAForumChannel = 40058,
ThereAreNoTagsAvailableThatCanBeSetByNonModerators = 40066,
ATagIsRequiredToCreateAForumPostInThisChannel = 40067,
InteractionHasAlreadyBeenAcknowledged = 40060,
TagNamesMustBeUnique = 40061,
+ #endregion
+
+ #region Action Preconditions/Checks (50XXX)
MissingPermissions = 50001,
InvalidAccountType = 50002,
CannotExecuteForDM = 50003,
- GuildWigitDisabled = 50004,
+ GuildWidgetDisabled = 50004,
CannotEditOtherUsersMessage = 50005,
CannotSendEmptyMessage = 50006,
CannotSendMessageToUser = 50007,
CannotSendMessageToVoiceChannel = 50008,
- ChannelVerificationTooHight = 50009,
+ ChannelVerificationTooHigh = 50009,
OAuth2ApplicationDoesntHaveBot = 50010,
OAuth2ApplicationLimitReached = 50011,
InvalidOAuth2State = 50012,
@@ -154,6 +154,7 @@ namespace Discord
BulkDeleteMessageTooOld = 50034,
InvalidFormBody = 50035,
InviteAcceptedForGuildThatBotIsntIn = 50036,
+ InvalidActivityAction = 50039,
InvalidAPIVersion = 50041,
FileUploadTooBig = 50045,
InvalidFileUpload = 50046,
@@ -161,6 +162,7 @@ namespace Discord
InvalidGuild = 50055,
InvalidMessageType = 50068,
PaymentSourceRequiredForGift = 50070,
+ CannotModifySystemWebhook = 50073,
CannotDeleteRequiredCommunityChannel = 50074,
CannotEditStickersWithinAMessage = 50080,
InvalidSticker = 50081,
@@ -172,9 +174,8 @@ namespace Discord
ServerRequiresMonetization = 50097,
ServerRequiresBoosts = 50101,
RequestBodyContainsInvalidJSON = 50109,
- FailedToResizeAssetBelowTheMaximumSize = 50138,
OwnershipCannotBeTransferredToABotUser = 50132,
- AssetResizeBelowTheMaximumSize= 50138,
+ FailedToResizeAssetBelowTheMaximumSize = 50138,
UploadedFileNotFound = 50146,
MissingPermissionToSendThisSticker = 50600,
#endregion
@@ -213,8 +214,8 @@ namespace Discord
LottieCantContainRasters = 170002,
StickerMaximumFramerateExceeded = 170003,
StickerMaximumFrameCountExceeded = 170004,
- LottieMaximumDimentionsExceeded = 170005,
- StickerFramerateBoundsExceeed = 170006,
+ LottieMaximumDimensionsExceeded = 170005,
+ StickerFramerateBoundsExceeded = 170006,
StickerAnimationDurationTooLong = 170007,
#endregion
@@ -222,7 +223,7 @@ namespace Discord
CannotUpdateFinishedEvent = 180000,
FailedStageCreation = 180002,
#endregion
-
+
#region Forum & Automod
MessageWasBlockedByAutomaticModeration = 200000,
TitleWasBlockedByAutomaticModeration = 200001,
diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs b/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs
new file mode 100644
index 000000000..37f34a90e
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs
@@ -0,0 +1,22 @@
+namespace Discord;
+
+///
+/// Represents public flags for a channel.
+///
+public enum ChannelFlags
+{
+ ///
+ /// Default value for flags, when none are given to a channel.
+ ///
+ None = 0,
+
+ ///
+ /// Flag given to a thread channel pinned on top of parent forum channel.
+ ///
+ Pinned = 1 << 1,
+
+ ///
+ /// Flag given to a forum channel that requires people to select tags when posting.
+ ///
+ RequireTag = 1 << 4
+}
diff --git a/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs
new file mode 100644
index 000000000..e1a123b37
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+
+namespace Discord;
+
+public class ForumChannelProperties : TextChannelProperties
+{
+
+ ///
+ /// Gets or sets the topic of the channel.
+ ///
+ ///
+ /// Not available in forum channels.
+ ///
+ public new Optional SlowModeInterval { get; }
+
+ ///
+ /// Gets or sets rate limit on creating posts in this forum channel.
+ ///
+ ///
+ /// Setting this value to anything above zero will require each user to wait X seconds before
+ /// creating another thread; setting this value to 0 will disable rate limits for this channel.
+ ///
+ /// Users with or
+ /// will be exempt from rate limits.
+ ///
+ ///
+ /// Thrown if the value does not fall within [0, 21600].
+ public Optional ThreadCreationInterval { get; set; }
+
+
+ ///
+ /// Gets or sets the default slow-mode for threads in this channel.
+ ///
+ ///
+ /// Setting this value to anything above zero will require each user to wait X seconds before
+ /// sending another message; setting this value to 0 will disable slow-mode for child threads.
+ ///
+ /// Users with or
+ /// will be exempt from slow-mode.
+ ///
+ ///
+ /// Thrown if the value does not fall within [0, 21600].
+ public Optional DefaultSlowModeInterval { get; set; }
+
+ ///
+ /// Gets or sets a collection of tags inside of this forum channel.
+ ///
+ public Optional> Tags { get; set; }
+
+ ///
+ /// Gets or sets a new default reaction emoji in this forum channel.
+ ///
+ public Optional DefaultReactionEmoji { get; set; }
+
+ ///
+ /// Gets or sets the rule used to order posts in forum channels.
+ ///
+ public Optional DefaultSortOrder { get; set; }
+}
diff --git a/src/Discord.Net.Core/Entities/Channels/ForumSortOrder.cs b/src/Discord.Net.Core/Entities/Channels/ForumSortOrder.cs
new file mode 100644
index 000000000..2a576d978
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/ForumSortOrder.cs
@@ -0,0 +1,17 @@
+namespace Discord;
+
+///
+/// Defines the rule used to order posts in forum channels.
+///
+public enum ForumSortOrder
+{
+ ///
+ /// Sort forum posts by activity.
+ ///
+ LatestActivity = 0,
+
+ ///
+ /// Sort forum posts by creation time (from most recent to oldest).
+ ///
+ CreationDate = 1
+}
diff --git a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs
index 339d6fffd..1e7d69c2d 100644
--- a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs
+++ b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs
@@ -36,5 +36,10 @@ namespace Discord
/// Gets or sets the permission overwrites for this channel.
///
public Optional> PermissionOverwrites { get; set; }
+
+ ///
+ /// Gets or sets the flags of the channel.
+ ///
+ public Optional Flags { get; set; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
index f4c6da2e2..55521bade 100644
--- a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Discord
{
- public interface IForumChannel : IGuildChannel, IMentionable
+ public interface IForumChannel : IGuildChannel, IMentionable, INestedChannel
{
///
/// Gets a value that indicates whether the channel is NSFW.
@@ -35,6 +35,55 @@ namespace Discord
///
IReadOnlyCollection Tags { get; }
+ ///
+ /// Gets the current rate limit on creating posts in this forum channel.
+ ///
+ ///
+ /// An representing the time in seconds required before the user can send another
+ /// message; 0 if disabled.
+ ///
+ int ThreadCreationInterval { get; }
+
+ ///
+ /// Gets the current default slow-mode delay for threads in this forum channel.
+ ///
+ ///
+ /// An representing the time in seconds required before the user can send another
+ /// message; 0 if disabled.
+ ///
+ int DefaultSlowModeInterval { get; }
+
+ ///
+ /// Gets the emoji to show in the add reaction button on a thread in a forum channel
+ ///
+ ///
+ /// If the emoji is only the will be populated.
+ /// Use to get the emoji.
+ ///
+ IEmote DefaultReactionEmoji { get; }
+
+ ///
+ /// Gets or sets the rule used to order posts in forum channels.
+ ///
+ ///
+ /// Defaults to null, which indicates a preferred sort order hasn't been set
+ ///
+ ForumSortOrder? DefaultSortOrder { get; }
+
+ ///
+ /// Modifies this forum channel.
+ ///
+ ///
+ /// This method modifies the current forum channel with the specified properties. To see an example of this
+ /// method and what properties are available, please refer to .
+ ///
+ /// The delegate containing the properties to modify the channel with.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous modification operation.
+ ///
+ Task ModifyAsync(Action func, RequestOptions options = null);
+
///
/// Creates a new post (thread) within the forum.
///
@@ -52,12 +101,13 @@ namespace Discord
/// A collection of stickers to send with the message.
/// A array of s to send with this response. Max 10.
/// A message flag to be applied to the sent message, only is permitted.
+ /// An array of to be applied to the post.
///
/// A task that represents the asynchronous creation operation.
///
Task CreatePostAsync(string title, ThreadArchiveDuration archiveDuration = ThreadArchiveDuration.OneDay, int? slowmode = null,
string text = null, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
- MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None);
+ MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None, ForumTag[] tags = null);
///
/// Creates a new post (thread) within the forum.
@@ -78,13 +128,14 @@ namespace Discord
/// A collection of stickers to send with the file.
/// A array of s to send with this response. Max 10.
/// A message flag to be applied to the sent message, only is permitted.
+ /// An array of to be applied to the post.
///
/// A task that represents the asynchronous creation operation.
///
Task CreatePostWithFileAsync(string title, string filePath, ThreadArchiveDuration archiveDuration = ThreadArchiveDuration.OneDay,
int? slowmode = null, string text = null, Embed embed = null, RequestOptions options = null, bool isSpoiler = false,
AllowedMentions allowedMentions = null, MessageComponent components = null,
- ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None);
+ ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None, ForumTag[] tags = null);
///
/// Creates a new post (thread) within the forum.
@@ -106,13 +157,14 @@ namespace Discord
/// A collection of stickers to send with the file.
/// A array of s to send with this response. Max 10.
/// A message flag to be applied to the sent message, only is permitted.
+ /// An array of to be applied to the post.
///
/// A task that represents the asynchronous creation operation.
///
public Task CreatePostWithFileAsync(string title, Stream stream, string filename, ThreadArchiveDuration archiveDuration = ThreadArchiveDuration.OneDay,
int? slowmode = null, string text = null, Embed embed = null, RequestOptions options = null, bool isSpoiler = false,
AllowedMentions allowedMentions = null, MessageComponent components = null,
- ISticker[] stickers = null, Embed[] embeds = null,MessageFlags flags = MessageFlags.None);
+ ISticker[] stickers = null, Embed[] embeds = null,MessageFlags flags = MessageFlags.None, ForumTag[] tags = null);
///
/// Creates a new post (thread) within the forum.
@@ -132,12 +184,13 @@ namespace Discord
/// A collection of stickers to send with the file.
/// A array of s to send with this response. Max 10.
/// A message flag to be applied to the sent message, only is permitted.
+ /// An array of to be applied to the post.
///
/// A task that represents the asynchronous creation operation.
///
public Task CreatePostWithFileAsync(string title, FileAttachment attachment, ThreadArchiveDuration archiveDuration = ThreadArchiveDuration.OneDay,
int? slowmode = null, string text = null, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
- MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None);
+ MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None, ForumTag[] tags = null);
///
/// Creates a new post (thread) within the forum.
@@ -155,14 +208,15 @@ namespace Discord
///
/// The message components to be included with this message. Used for interactions.
/// A collection of stickers to send with the file.
- /// A array of s to send with this response. Max 10.
+ /// An array of s to send with this response. Max 10.
/// A message flag to be applied to the sent message, only is permitted.
+ /// An array of to be applied to the post.
///
/// A task that represents the asynchronous creation operation.
///
public Task CreatePostWithFilesAsync(string title, IEnumerable attachments, ThreadArchiveDuration archiveDuration = ThreadArchiveDuration.OneDay,
int? slowmode = null, string text = null, Embed embed = null, RequestOptions options = null, AllowedMentions allowedMentions = null,
- MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None);
+ MessageComponent components = null, ISticker[] stickers = null, Embed[] embeds = null, MessageFlags flags = MessageFlags.None, ForumTag[] tags = null);
///
/// Gets a collection of active threads within this forum channel.
diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
index 992bd71fc..12874f2c2 100644
--- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
@@ -21,6 +21,17 @@ namespace Discord
///
int Position { get; }
+ ///
+ /// Gets the flags related to this channel.
+ ///
+ ///
+ /// This value is determined by bitwise OR-ing values together.
+ ///
+ ///
+ /// A channel's flags, if any is associated.
+ ///
+ ChannelFlags Flags { get; }
+
///
/// Gets the guild associated with this channel.
///
diff --git a/src/Discord.Net.Core/Entities/Channels/IThreadChannel.cs b/src/Discord.Net.Core/Entities/Channels/IThreadChannel.cs
index f03edbbf9..52df07dcc 100644
--- a/src/Discord.Net.Core/Entities/Channels/IThreadChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IThreadChannel.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
namespace Discord
@@ -56,6 +57,14 @@ namespace Discord
///
bool? IsInvitable { get; }
+ ///
+ /// Gets ids of tags applied to a forum thread
+ ///
+ ///
+ /// This property is only available on forum threads.
+ ///
+ IReadOnlyCollection AppliedTags { get; }
+
///
/// Gets when the thread was created.
///
@@ -102,5 +111,16 @@ namespace Discord
/// A task that represents the asynchronous operation of removing a user from this thread.
///
Task RemoveUserAsync(IGuildUser user, RequestOptions options = null);
+
+ ///
+ /// Modifies this thread channel.
+ ///
+ /// The delegate containing the properties to modify the channel with.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous modification operation.
+ ///
+ ///
+ Task ModifyAsync(Action func, RequestOptions options = null);
}
}
diff --git a/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs
index 2dceb025c..acd69f480 100644
--- a/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs
+++ b/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
namespace Discord
{
@@ -39,20 +40,10 @@ namespace Discord
/// Thrown if the value does not fall within [0, 21600].
public Optional SlowModeInterval { get; set; }
- ///
- /// Gets or sets whether or not the thread is archived.
- ///
- public Optional Archived { get; set; }
-
- ///
- /// Gets or sets whether or not the thread is locked.
- ///
- public Optional Locked { get; set; }
-
///
/// Gets or sets the auto archive duration.
///
public Optional AutoArchiveDuration { get; set; }
-
+
}
}
diff --git a/src/Discord.Net.Core/Entities/Channels/ThreadChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/ThreadChannelProperties.cs
new file mode 100644
index 000000000..af5c44129
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/ThreadChannelProperties.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+
+namespace Discord;
+
+
+///
+/// Provides properties that are used to modify an with the specified changes.
+///
+///
+public class ThreadChannelProperties : TextChannelProperties
+{
+ ///
+ /// Gets or sets the tags applied to a forum thread
+ ///
+ public Optional> AppliedTags { get; set; }
+
+ ///
+ /// Gets or sets whether or not the thread is locked.
+ ///
+ public Optional Locked { get; set; }
+
+ ///
+ /// Gets or sets whether or not the thread is archived.
+ ///
+ public Optional Archived { get; set; }
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTag.cs b/src/Discord.Net.Core/Entities/ForumTag.cs
deleted file mode 100644
index 26ae4301e..000000000
--- a/src/Discord.Net.Core/Entities/ForumTag.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Discord
-{
- ///
- /// A struct representing a forum channel tag.
- ///
- public struct ForumTag
- {
- ///
- /// Gets the Id of the tag.
- ///
- public ulong Id { get; }
-
- ///
- /// Gets the name of the tag.
- ///
- public string Name { get; }
-
- ///
- /// Gets the emoji of the tag or if none is set.
- ///
- public IEmote Emoji { get; }
-
- internal ForumTag(ulong id, string name, ulong? emojiId, string emojiName)
- {
- if (emojiId.HasValue && emojiId.Value != 0)
- Emoji = new Emote(emojiId.Value, emojiName, false);
- else if (emojiName != null)
- Emoji = new Emoji(name);
- else
- Emoji = null;
-
- Id = id;
- Name = name;
- }
- }
-}
diff --git a/src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs b/src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs
new file mode 100644
index 000000000..afdb99bf7
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+#nullable enable
+
+namespace Discord
+{
+ ///
+ /// A struct representing a forum channel tag.
+ ///
+ public struct ForumTag : ISnowflakeEntity, IForumTag
+ {
+ ///
+ /// Gets the Id of the tag.
+ ///
+ public ulong Id { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public IEmote? Emoji { get; }
+
+ ///
+ public bool IsModerated { get; }
+
+ ///
+ public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
+
+ internal ForumTag(ulong id, string name, ulong? emojiId = null, string? emojiName = null, bool moderated = false)
+ {
+ if (emojiId.HasValue && emojiId.Value != 0)
+ Emoji = new Emote(emojiId.Value, null, false);
+ else if (emojiName != null)
+ Emoji = new Emoji(emojiName);
+ else
+ Emoji = null;
+
+ Id = id;
+ Name = name;
+ IsModerated = moderated;
+ }
+
+ public override int GetHashCode() => (Id, Name, Emoji, IsModerated).GetHashCode();
+
+ public override bool Equals(object? obj)
+ => obj is ForumTag tag && Equals(tag);
+
+ ///
+ /// Gets whether supplied tag is equals to the current one.
+ ///
+ public bool Equals(ForumTag tag)
+ => Id == tag.Id &&
+ Name == tag.Name &&
+ (Emoji is Emoji emoji && tag.Emoji is Emoji otherEmoji && emoji.Equals(otherEmoji) ||
+ Emoji is Emote emote && tag.Emoji is Emote otherEmote && emote.Equals(otherEmote)) &&
+ IsModerated == tag.IsModerated;
+
+ public static bool operator ==(ForumTag? left, ForumTag? right)
+ => left?.Equals(right) ?? right is null;
+
+ public static bool operator !=(ForumTag? left, ForumTag? right) => !(left == right);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilder.cs b/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilder.cs
new file mode 100644
index 000000000..d8e881189
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilder.cs
@@ -0,0 +1,191 @@
+#nullable enable
+using System;
+
+namespace Discord;
+
+public class ForumTagBuilder
+{
+ private string? _name;
+ private IEmote? _emoji;
+ private bool _moderated;
+ private ulong? _id;
+
+ ///
+ /// Returns the maximum length of name allowed by Discord.
+ ///
+ public const int MaxNameLength = 20;
+
+ ///
+ /// Gets or sets the snowflake Id of the tag.
+ ///
+ ///
+ /// If set this will update existing tag or will create a new one otherwise.
+ ///
+ public ulong? Id
+ {
+ get { return _id; }
+ set { _id = value; }
+ }
+
+ ///
+ /// Gets or sets the name of the tag.
+ ///
+ /// Name length must be less than or equal to .
+ public string? Name
+ {
+ get { return _name; }
+ set
+ {
+ if (value?.Length > MaxNameLength)
+ throw new ArgumentException(message: $"Name length must be less than or equal to {MaxNameLength}.", paramName: nameof(Name));
+ _name = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the emoji of the tag.
+ ///
+ public IEmote? Emoji
+ {
+ get { return _emoji; }
+ set { _emoji = value; }
+ }
+
+ ///
+ /// Gets or sets whether this tag can only be added to or removed from threads by a member
+ /// with the permission
+ ///
+ public bool IsModerated
+ {
+ get { return _moderated; }
+ set { _moderated = value; }
+ }
+
+ ///
+ /// Initializes a new class.
+ ///
+ public ForumTagBuilder()
+ {
+
+ }
+
+ ///
+ /// Initializes a new class with values
+ ///
+ /// If set existing tag will be updated or a new one will be created otherwise.
+ /// Name of the tag.
+ /// Sets whether this tag can only be added to or removed from threads by a member
+ /// with the permission.
+ public ForumTagBuilder(string name, ulong? id = null, bool isModerated = false)
+ {
+ Name = name;
+ IsModerated = isModerated;
+ Id = id;
+ }
+
+ ///
+ /// Initializes a new class with values
+ ///
+ /// Name of the tag.
+ /// If set existing tag will be updated or a new one will be created otherwise.
+ /// Display emoji of the tag.
+ /// Sets whether this tag can only be added to or removed from threads by a member
+ /// with the permission.
+ public ForumTagBuilder(string name, ulong? id = null, bool isModerated = false, IEmote? emoji = null)
+ {
+ Name = name;
+ Emoji = emoji;
+ IsModerated = isModerated;
+ Id = id;
+ }
+
+ ///
+ /// Initializes a new class with values
+ ///
+ /// /// Name of the tag.
+ /// If set existing tag will be updated or a new one will be created otherwise.
+ /// The id of custom Display emoji of the tag.
+ /// Sets whether this tag can only be added to or removed from threads by a member
+ /// with the permission
+ public ForumTagBuilder(string name, ulong? id = null, bool isModerated = false, ulong? emoteId = null)
+ {
+ Name = name;
+ if(emoteId is not null)
+ Emoji = new Emote(emoteId.Value, null, false);
+ IsModerated = isModerated;
+ Id = id;
+ }
+
+ ///
+ /// Builds the Tag.
+ ///
+ /// An instance of
+ /// "Name must be set to build the tag"
+ public ForumTagProperties Build()
+ {
+ if (_name is null)
+ throw new ArgumentNullException(nameof(Name), "Name must be set to build the tag");
+ return new ForumTagProperties(_name!, _emoji, _moderated);
+ }
+
+ ///
+ /// Sets the name of the tag.
+ ///
+ /// Name length must be less than or equal to .
+ public ForumTagBuilder WithName(string name)
+ {
+ Name = name;
+ return this;
+ }
+
+ ///
+ /// Sets the id of the tag.
+ ///
+ /// If set existing tag will be updated or a new one will be created otherwise.
+ /// Name length must be less than or equal to .
+ public ForumTagBuilder WithId(ulong? id)
+ {
+ Id = id;
+ return this;
+ }
+
+ ///
+ /// Sets the emoji of the tag.
+ ///
+ public ForumTagBuilder WithEmoji(IEmote? emoji)
+ {
+ Emoji = emoji;
+ return this;
+ }
+
+ ///
+ /// Sets whether this tag can only be added to or removed from threads by a member
+ /// with the permission
+ ///
+ public ForumTagBuilder WithModerated(bool moderated)
+ {
+ IsModerated = moderated;
+ return this;
+ }
+
+ public override int GetHashCode() => base.GetHashCode();
+
+ public override bool Equals(object? obj)
+ => obj is ForumTagBuilder builder && Equals(builder);
+
+ ///
+ /// Gets whether supplied tag builder is equals to the current one.
+ ///
+ public bool Equals(ForumTagBuilder? builder)
+ => builder is not null &&
+ Id == builder.Id &&
+ Name == builder.Name &&
+ (Emoji is Emoji emoji && builder.Emoji is Emoji otherEmoji && emoji.Equals(otherEmoji) ||
+ Emoji is Emote emote && builder.Emoji is Emote otherEmote && emote.Equals(otherEmote)) &&
+ IsModerated == builder.IsModerated;
+
+ public static bool operator ==(ForumTagBuilder? left, ForumTagBuilder? right)
+ => left?.Equals(right) ?? right is null ;
+
+ public static bool operator !=(ForumTagBuilder? left, ForumTagBuilder? right) => !(left == right);
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilderExtensions.cs b/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilderExtensions.cs
new file mode 100644
index 000000000..73a953fe6
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilderExtensions.cs
@@ -0,0 +1,11 @@
+namespace Discord;
+
+public static class ForumTagBuilderExtensions
+{
+ public static ForumTagBuilder ToForumTagBuilder(this ForumTag tag)
+ => new ForumTagBuilder(tag.Name, tag.Id, tag.IsModerated, tag.Emoji);
+
+ public static ForumTagBuilder ToForumTagBuilder(this ForumTagProperties tag)
+ => new ForumTagBuilder(tag.Name, tag.Id, tag.IsModerated, tag.Emoji);
+
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTags/ForumTagProperties.cs b/src/Discord.Net.Core/Entities/ForumTags/ForumTagProperties.cs
new file mode 100644
index 000000000..6ded49204
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTagProperties.cs
@@ -0,0 +1,48 @@
+namespace Discord;
+
+#nullable enable
+
+public class ForumTagProperties : IForumTag
+{
+ ///
+ /// Gets the Id of the tag.
+ ///
+ public ulong Id { get; }
+
+ ///
+ public string Name { get; }
+
+ ///
+ public IEmote? Emoji { get; }
+
+ ///
+ public bool IsModerated { get; }
+
+ internal ForumTagProperties(string name, IEmote? emoji = null, bool isMmoderated = false)
+ {
+ Name = name;
+ Emoji = emoji;
+ IsModerated = isMmoderated;
+ }
+
+ public override int GetHashCode() => (Id, Name, Emoji, IsModerated).GetHashCode();
+
+ public override bool Equals(object? obj)
+ => obj is ForumTagProperties tag && Equals(tag);
+
+ ///
+ /// Gets whether supplied tag is equals to the current one.
+ ///
+ public bool Equals(ForumTagProperties? tag)
+ => tag is not null &&
+ Id == tag.Id &&
+ Name == tag.Name &&
+ (Emoji is Emoji emoji && tag.Emoji is Emoji otherEmoji && emoji.Equals(otherEmoji) ||
+ Emoji is Emote emote && tag.Emoji is Emote otherEmote && emote.Equals(otherEmote)) &&
+ IsModerated == tag.IsModerated;
+
+ public static bool operator ==(ForumTagProperties? left, ForumTagProperties? right)
+ => left?.Equals(right) ?? right is null;
+
+ public static bool operator !=(ForumTagProperties? left, ForumTagProperties? right) => !(left == right);
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTags/IForumTag.cs b/src/Discord.Net.Core/Entities/ForumTags/IForumTag.cs
new file mode 100644
index 000000000..8b8b866b2
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/IForumTag.cs
@@ -0,0 +1,29 @@
+namespace Discord;
+
+#nullable enable
+
+///
+/// Represents a Discord forum tag
+///
+public interface IForumTag
+{
+ ///
+ /// Gets the name of the tag.
+ ///
+ string Name { get; }
+
+ ///
+ /// Gets the emoji of the tag or if none is set.
+ ///
+ ///
+ /// If the emoji is only the will be populated.
+ /// Use to get the emoji.
+ ///
+ IEmote? Emoji { get; }
+
+ ///
+ /// Gets whether this tag can only be added to or removed from threads by a member
+ /// with the permission
+ ///
+ bool IsModerated { get; }
+}
diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs b/src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs
index 52a70a6f5..8ec3ee2ae 100644
--- a/src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/GuildFeature.cs
@@ -181,5 +181,9 @@ namespace Discord
/// The guild has enabled the welcome screen.
///
WelcomeScreenEnabled = 1L << 41,
+ ///
+ /// The guild has been set as a support server on the App Directory.
+ ///
+ DeveloperSupportServer = 1L << 42,
}
}
diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index 34a08f1e7..d1ff7b99c 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -761,6 +761,18 @@ namespace Discord
///
Task CreateCategoryAsync(string name, Action func = null, RequestOptions options = null);
+ ///
+ /// Creates a new channel forum in this guild.
+ ///
+ /// The new name for the forum.
+ /// The delegate containing the properties to be applied to the channel upon its creation.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents the asynchronous creation operation. The task result contains the newly created
+ /// forum channel.
+ ///
+ Task CreateForumChannelAsync(string name, Action func = null, RequestOptions options = null);
+
///
/// Gets a collection of all the voice regions this guild can access.
///
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionType.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionType.cs
index 4506b66d9..2bad7fcb7 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionType.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionType.cs
@@ -21,7 +21,7 @@ namespace Discord
String = 3,
///
- /// An .
+ /// An .
///
Integer = 4,
diff --git a/src/Discord.Net.Core/Entities/Messages/TimestampTag.cs b/src/Discord.Net.Core/Entities/Messages/TimestampTag.cs
index 3beffdbb6..1ca6dc41c 100644
--- a/src/Discord.Net.Core/Entities/Messages/TimestampTag.cs
+++ b/src/Discord.Net.Core/Entities/Messages/TimestampTag.cs
@@ -5,17 +5,28 @@ namespace Discord
///
/// Represents a class used to make timestamps in messages. see .
///
- public class TimestampTag
+ public readonly struct TimestampTag
{
///
- /// Gets or sets the style of the timestamp tag.
+ /// Gets the time for this timestamp tag.
///
- public TimestampTagStyles Style { get; set; } = TimestampTagStyles.ShortDateTime;
+ public DateTimeOffset Time { get; }
///
- /// Gets or sets the time for this timestamp tag.
+ /// Gets the style of this tag. if none was provided.
///
- public DateTimeOffset Time { get; set; }
+ public TimestampTagStyles? Style { get; }
+
+ ///
+ /// Creates a new from the provided time.
+ ///
+ /// The time for this timestamp tag.
+ /// The style for this timestamp tag.
+ public TimestampTag(DateTimeOffset time, TimestampTagStyles? style = null)
+ {
+ Time = time;
+ Style = style;
+ }
///
/// Converts the current timestamp tag to the string representation supported by discord.
@@ -23,11 +34,23 @@ namespace Discord
/// If the is null then the default 0 will be used.
///
///
+ ///
+ /// Will use the provided if provided. If this value is null, it will default to .
+ ///
/// A string that is compatible in a discord message, ex: <t:1625944201:f>
public override string ToString()
- {
- return $"";
- }
+ => ToString(Style ?? TimestampTagStyles.ShortDateTime);
+
+ ///
+ /// Converts the current timestamp tag to the string representation supported by discord.
+ ///
+ /// If the is null then the default 0 will be used.
+ ///
+ ///
+ /// The formatting style for this tag.
+ /// A string that is compatible in a discord message, ex: <t:1625944201:f>
+ public string ToString(TimestampTagStyles style)
+ => $"";
///
/// Creates a new timestamp tag with the specified object.
@@ -35,14 +58,8 @@ namespace Discord
/// The time of this timestamp tag.
/// The style for this timestamp tag.
/// The newly create timestamp tag.
- public static TimestampTag FromDateTime(DateTime time, TimestampTagStyles style = TimestampTagStyles.ShortDateTime)
- {
- return new TimestampTag
- {
- Style = style,
- Time = time
- };
- }
+ public static TimestampTag FromDateTime(DateTime time, TimestampTagStyles? style = null)
+ => new(time, style);
///
/// Creates a new timestamp tag with the specified object.
@@ -50,13 +67,25 @@ namespace Discord
/// The time of this timestamp tag.
/// The style for this timestamp tag.
/// The newly create timestamp tag.
- public static TimestampTag FromDateTimeOffset(DateTimeOffset time, TimestampTagStyles style = TimestampTagStyles.ShortDateTime)
- {
- return new TimestampTag
- {
- Style = style,
- Time = time
- };
- }
+ public static TimestampTag FromDateTimeOffset(DateTimeOffset time, TimestampTagStyles? style = null)
+ => new(time, style);
+
+ ///
+ /// Immediately formats the provided time and style into a timestamp string.
+ ///
+ /// The time of this timestamp tag.
+ /// The style for this timestamp tag.
+ /// The newly create timestamp string.
+ public static string FormatFromDateTime(DateTime time, TimestampTagStyles style)
+ => FormatFromDateTimeOffset(time, style);
+
+ ///
+ /// Immediately formats the provided time and style into a timestamp string.
+ ///
+ /// The time of this timestamp tag.
+ /// The style for this timestamp tag.
+ /// The newly create timestamp string.
+ public static string FormatFromDateTimeOffset(DateTimeOffset time, TimestampTagStyles style)
+ => $"";
}
-}
\ No newline at end of file
+}
diff --git a/src/Discord.Net.Core/Entities/Users/PremiumType.cs b/src/Discord.Net.Core/Entities/Users/PremiumType.cs
index 2b41e0b6a..24165d4e9 100644
--- a/src/Discord.Net.Core/Entities/Users/PremiumType.cs
+++ b/src/Discord.Net.Core/Entities/Users/PremiumType.cs
@@ -16,6 +16,11 @@ namespace Discord
///
/// Nitro subscription. Includes app perks as well as the games subscription service.
///
- Nitro = 2
+ Nitro = 2,
+
+ ///
+ /// Nitro Basic subscription. Includes app perks like video backgrounds, sending bigger files.
+ ///
+ NitroBasic = 3
}
}
diff --git a/src/Discord.Net.Core/Extensions/ChannelExtensions.cs b/src/Discord.Net.Core/Extensions/ChannelExtensions.cs
index b5ddae1cf..a24588792 100644
--- a/src/Discord.Net.Core/Extensions/ChannelExtensions.cs
+++ b/src/Discord.Net.Core/Extensions/ChannelExtensions.cs
@@ -46,6 +46,9 @@ namespace Discord
case ITextChannel:
return ChannelType.Text;
+
+ case IForumChannel:
+ return ChannelType.Forum;
}
return null;
diff --git a/src/Discord.Net.Interactions/Info/Commands/AutocompleteCommandInfo.cs b/src/Discord.Net.Interactions/Info/Commands/AutocompleteCommandInfo.cs
index 9e30c55f4..b8940f42f 100644
--- a/src/Discord.Net.Interactions/Info/Commands/AutocompleteCommandInfo.cs
+++ b/src/Discord.Net.Interactions/Info/Commands/AutocompleteCommandInfo.cs
@@ -23,7 +23,7 @@ namespace Discord.Interactions
public string CommandName { get; }
///
- public override IReadOnlyCollection Parameters { get; }
+ public override IReadOnlyList Parameters { get; }
///
public override bool SupportsWildCards => false;
@@ -41,9 +41,12 @@ namespace Discord.Interactions
if (context.Interaction is not IAutocompleteInteraction)
return ExecuteResult.FromError(InteractionCommandError.ParseFailed, $"Provided {nameof(IInteractionContext)} doesn't belong to a Autocomplete Interaction");
- return await RunAsync(context, Array.Empty