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..243f18cb5
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs
@@ -0,0 +1,50 @@
+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; }
+}
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 fb6211615..6e1da6e96 100644
--- a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
@@ -53,6 +53,20 @@ namespace Discord
///
int DefaultSlowModeInterval { 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.
///
diff --git a/src/Discord.Net.Core/Entities/ForumTag.cs b/src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs
similarity index 74%
rename from src/Discord.Net.Core/Entities/ForumTag.cs
rename to src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs
index aef983555..5b962294a 100644
--- a/src/Discord.Net.Core/Entities/ForumTag.cs
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTag.cs
@@ -4,6 +4,8 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
+#nullable enable
+
namespace Discord
{
///
@@ -24,23 +26,27 @@ namespace Discord
///
/// Gets the emoji of the tag or if none is set.
///
- public IEmote Emoji { get; }
+ ///
+ /// If the emoji is only the will be populated.
+ /// Use to get the emoji.
+ ///
+ public IEmote? Emoji { get; }
///
/// Gets whether this tag can only be added to or removed from threads by a member
/// with the permission
///
- public bool Moderated { get; }
+ public bool IsModerated { get; }
///
/// Gets when the tag was created.
///
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
- internal ForumTag(ulong id, string name, ulong? emojiId, string emojiName, bool moderated)
+ internal ForumTag(ulong id, string name, ulong? emojiId, string? emojiName, bool moderated)
{
if (emojiId.HasValue && emojiId.Value != 0)
- Emoji = new Emote(emojiId.Value, emojiName, false);
+ Emoji = new Emote(emojiId.Value, null, false);
else if (emojiName != null)
Emoji = new Emoji(name);
else
@@ -48,7 +54,7 @@ namespace Discord
Id = id;
Name = name;
- Moderated = moderated;
+ IsModerated = moderated;
}
}
}
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..19d0aa639
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTagBuilder.cs
@@ -0,0 +1,128 @@
+#nullable enable
+using System;
+
+namespace Discord;
+
+public class ForumTagBuilder
+{
+ private string? _name;
+ private IEmote? _emoji;
+ private bool _moderated;
+
+ ///
+ /// Returns the maximum length of name allowed by Discord.
+ ///
+ public const int MaxNameLength = 20;
+
+ ///
+ /// 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
+ ///
+ public ForumTagBuilder(string name)
+ {
+ Name = name;
+ IsModerated = false;
+ }
+
+ ///
+ /// Initializes a new class with values
+ ///
+ public ForumTagBuilder(string name, IEmote? emoji = null, bool moderated = false)
+ {
+ Name = name;
+ Emoji = emoji;
+ IsModerated = moderated;
+ }
+
+ ///
+ /// Initializes a new class with values
+ ///
+ public ForumTagBuilder(string name, ulong? emoteId = null, bool moderated = false)
+ {
+ Name = name;
+ if(emoteId is not null)
+ Emoji = new Emote(emoteId.Value, string.Empty, false);
+ IsModerated = moderated;
+ }
+
+ ///
+ /// 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 emoji of the tag.
+ ///
+ public ForumTagBuilder WithEmoji(IEmote? emoji)
+ {
+ Emoji = emoji;
+ return this;
+ }
+
+ ///
+ /// Sets the IsModerated of the tag.
+ ///
+ public ForumTagBuilder WithModerated(bool moderated)
+ {
+ IsModerated = moderated;
+ return this;
+ }
+}
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..5bf7d2b85
--- /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.Emoji, tag.IsModerated);
+
+ public static ForumTagBuilder ToForumTagBuilder(this ForumTagProperties tag)
+ => new ForumTagBuilder(tag.Name, tag.Emoji, tag.IsModerated);
+
+}
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..2770438be
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTags/ForumTagProperties.cs
@@ -0,0 +1,29 @@
+namespace Discord;
+
+#nullable enable
+
+public class ForumTagProperties
+{
+ ///
+ /// 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; }
+
+ ///
+ /// Gets whether this tag can only be added to or removed from threads by a member
+ /// with the permission
+ ///
+ public bool IsModerated { get; }
+
+ internal ForumTagProperties(string name, IEmote? emoji = null, bool isMmoderated = false)
+ {
+ Name = name;
+ Emoji = emoji;
+ IsModerated = isMmoderated;
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Rest/ForumTagParams.cs b/src/Discord.Net.Rest/API/Rest/ForumTagParams.cs
new file mode 100644
index 000000000..86f77816f
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/ForumTagParams.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+
+namespace Discord.API
+{
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ internal class ForumTagParams
+ {
+
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ [JsonProperty("emoji_id")]
+ public Optional EmojiId { get; set; }
+
+ [JsonProperty("emoji_name")]
+ public Optional EmojiName { get; set; }
+
+ [JsonProperty("moderated")]
+ public bool Moderated { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyForumChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyForumChannelParams.cs
new file mode 100644
index 000000000..9d97e80fa
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Rest/ModifyForumChannelParams.cs
@@ -0,0 +1,17 @@
+using Newtonsoft.Json;
+
+namespace Discord.API.Rest;
+
+
+[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+internal class ModifyForumChannelParams : ModifyTextChannelParams
+{
+ [JsonProperty("available_tags")]
+ public Optional Tags { get; set; }
+
+ [JsonProperty("default_thread_rate_limit_per_user")]
+ public Optional DefaultSlowModeInterval { get; set; }
+
+ [JsonProperty("rate_limit_per_user")]
+ public Optional ThreadCreationInterval { get; set; }
+}
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs
index dfe9cd980..dea0c037f 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs
@@ -13,5 +13,7 @@ namespace Discord.API.Rest
public Optional CategoryId { get; set; }
[JsonProperty("permission_overwrites")]
public Optional Overwrites { get; set; }
+ [JsonProperty("flags")]
+ public Optional Flags { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Rest/ModifyThreadParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyThreadParams.cs
index 53c1f4f5f..bd651b22c 100644
--- a/src/Discord.Net.Rest/API/Rest/ModifyThreadParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/ModifyThreadParams.cs
@@ -22,5 +22,8 @@ namespace Discord.API.Rest
[JsonProperty("applied_tags")]
public Optional> AppliedTags { get; set; }
+
+ [JsonProperty("flags")]
+ public Optional Flags { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
index d66fd5e51..4e353c39b 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
@@ -38,6 +38,7 @@ namespace Discord.Rest
Deny = overwrite.Permissions.DenyValue.ToString()
}).ToArray()
: Optional.Create(),
+ Flags = args.Flags.GetValueOrDefault(),
};
return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs
new file mode 100644
index 000000000..aeb7c22a4
--- /dev/null
+++ b/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Model = Discord.API.Channel;
+
+namespace Discord.Rest;
+
+internal static class ForumHelper
+{
+ public static async Task ModifyAsync(IForumChannel channel, BaseDiscordClient client,
+ Action func,
+ RequestOptions options)
+ {
+ var args = new ForumChannelProperties();
+ func(args);
+ var apiArgs = new API.Rest.ModifyForumChannelParams()
+ {
+ Name = args.Name,
+ Position = args.Position,
+ CategoryId = args.CategoryId,
+ Overwrites = args.PermissionOverwrites.IsSpecified
+ ? args.PermissionOverwrites.Value.Select(overwrite => new API.Overwrite
+ {
+ TargetId = overwrite.TargetId,
+ TargetType = overwrite.TargetType,
+ Allow = overwrite.Permissions.AllowValue.ToString(),
+ Deny = overwrite.Permissions.DenyValue.ToString()
+ }).ToArray()
+ : Optional.Create(),
+ DefaultSlowModeInterval = args.DefaultSlowModeInterval,
+ ThreadCreationInterval = args.ThreadCreationInterval,
+ Tags = args.Tags.IsSpecified
+ ? args.Tags.Value.Select(tag => new API.ForumTagParams
+ {
+ Name = tag.Name,
+ EmojiId = tag.Emoji is Emote emote
+ ? emote.Id
+ : Optional.Unspecified,
+ EmojiName = tag.Emoji is Emoji emoji
+ ? emoji.Name
+ : Optional.Unspecified
+ }).ToArray()
+ : Optional.Create(),
+ Flags = args.Flags.GetValueOrDefault(),
+ Topic = args.Topic,
+ };
+ return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
+ }
+}
diff --git a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
index 574b12491..5c4ce817e 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
@@ -58,7 +58,8 @@ namespace Discord.Rest
AutoArchiveDuration = args.AutoArchiveDuration,
Locked = args.Locked,
Slowmode = args.SlowModeInterval,
- AppliedTags = args.AppliedTags
+ AppliedTags = args.AppliedTags,
+ Flags = args.Flags,
};
return await client.ApiClient.ModifyThreadAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
}
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
index aa4ccb9ae..9347d0327 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
@@ -63,6 +63,10 @@ namespace Discord.WebSocket
).ToImmutableArray();
}
+ ///
+ public virtual Task ModifyAsync(Action func, RequestOptions options = null)
+ => ForumHelper.ModifyAsync(this, Discord, func, options);
+
///
public 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)
=> ThreadHelper.CreatePostAsync(this, Discord, title, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);