added: - channel flags - `ForumTagBuilder` - imroved channel modificationpull/2469/head
@@ -0,0 +1,22 @@ | |||||
namespace Discord; | |||||
/// <summary> | |||||
/// Represents public flags for a channel. | |||||
/// </summary> | |||||
public enum ChannelFlags | |||||
{ | |||||
/// <summary> | |||||
/// Default value for flags, when none are given to a channel. | |||||
/// </summary> | |||||
None = 0, | |||||
/// <summary> | |||||
/// Flag given to a thread channel pinned on top of parent forum channel. | |||||
/// </summary> | |||||
Pinned = 1 << 1, | |||||
/// <summary> | |||||
/// Flag given to a forum channel that requires people to select tags when posting. | |||||
/// </summary> | |||||
RequireTag = 1 << 4 | |||||
} |
@@ -0,0 +1,50 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Discord; | |||||
public class ForumChannelProperties : TextChannelProperties | |||||
{ | |||||
/// <summary> | |||||
/// Gets or sets the topic of the channel. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Not available in forum channels. | |||||
/// </remarks> | |||||
public new Optional<int> SlowModeInterval { get; } | |||||
/// <summary> | |||||
/// Gets or sets rate limit on creating posts in this forum channel. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Setting this value to anything above zero will require each user to wait X seconds before | |||||
/// creating another thread; setting this value to <c>0</c> will disable rate limits for this channel. | |||||
/// <note> | |||||
/// Users with <see cref="Discord.ChannelPermission.ManageMessages"/> or | |||||
/// <see cref="ChannelPermission.ManageChannels"/> will be exempt from rate limits. | |||||
/// </note> | |||||
/// </remarks> | |||||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the value does not fall within [0, 21600].</exception> | |||||
public Optional<int> ThreadCreationInterval { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets the default slow-mode for threads in this channel. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// Setting this value to anything above zero will require each user to wait X seconds before | |||||
/// sending another message; setting this value to <c>0</c> will disable slow-mode for child threads. | |||||
/// <note> | |||||
/// Users with <see cref="Discord.ChannelPermission.ManageMessages"/> or | |||||
/// <see cref="ChannelPermission.ManageChannels"/> will be exempt from slow-mode. | |||||
/// </note> | |||||
/// </remarks> | |||||
/// <exception cref="ArgumentOutOfRangeException">Thrown if the value does not fall within [0, 21600].</exception> | |||||
public Optional<int> DefaultSlowModeInterval { get; set; } | |||||
/// <summary> | |||||
/// Gets or sets a collection of tags inside of this forum channel. | |||||
/// </summary> | |||||
public Optional<IEnumerable<ForumTagProperties>> Tags { get; set; } | |||||
} |
@@ -36,5 +36,10 @@ namespace Discord | |||||
/// Gets or sets the permission overwrites for this channel. | /// Gets or sets the permission overwrites for this channel. | ||||
/// </summary> | /// </summary> | ||||
public Optional<IEnumerable<Overwrite>> PermissionOverwrites { get; set; } | public Optional<IEnumerable<Overwrite>> PermissionOverwrites { get; set; } | ||||
/// <summary> | |||||
/// Gets or sets the flags of the channel. | |||||
/// </summary> | |||||
public Optional<ChannelFlags> Flags { get; set; } | |||||
} | } | ||||
} | } |
@@ -53,6 +53,20 @@ namespace Discord | |||||
/// </returns> | /// </returns> | ||||
int DefaultSlowModeInterval { get; } | int DefaultSlowModeInterval { get; } | ||||
/// <summary> | |||||
/// Modifies this forum channel. | |||||
/// </summary> | |||||
/// <remarks> | |||||
/// 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 <see cref="ForumChannelProperties"/>. | |||||
/// </remarks> | |||||
/// <param name="func">The delegate containing the properties to modify the channel with.</param> | |||||
/// <param name="options">The options to be used when sending the request.</param> | |||||
/// <returns> | |||||
/// A task that represents the asynchronous modification operation. | |||||
/// </returns> | |||||
Task ModifyAsync(Action<ForumChannelProperties> func, RequestOptions options = null); | |||||
/// <summary> | /// <summary> | ||||
/// Creates a new post (thread) within the forum. | /// Creates a new post (thread) within the forum. | ||||
/// </summary> | /// </summary> | ||||
@@ -4,6 +4,8 @@ using System.Linq; | |||||
using System.Text; | using System.Text; | ||||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
#nullable enable | |||||
namespace Discord | namespace Discord | ||||
{ | { | ||||
/// <summary> | /// <summary> | ||||
@@ -24,23 +26,27 @@ namespace Discord | |||||
/// <summary> | /// <summary> | ||||
/// Gets the emoji of the tag or <see langword="null"/> if none is set. | /// Gets the emoji of the tag or <see langword="null"/> if none is set. | ||||
/// </summary> | /// </summary> | ||||
public IEmote Emoji { get; } | |||||
/// <remarks> | |||||
/// If the emoji is <see cref="Emote"/> only the <see cref="Emote.Id"/> will be populated. | |||||
/// Use <see cref="IGuild.GetEmoteAsync"/> to get the emoji. | |||||
/// </remarks> | |||||
public IEmote? Emoji { get; } | |||||
/// <summary> | /// <summary> | ||||
/// Gets whether this tag can only be added to or removed from threads by a member | /// Gets whether this tag can only be added to or removed from threads by a member | ||||
/// with the <see cref="GuildPermissions.ManageThreads"/> permission | /// with the <see cref="GuildPermissions.ManageThreads"/> permission | ||||
/// </summary> | /// </summary> | ||||
public bool Moderated { get; } | |||||
public bool IsModerated { get; } | |||||
/// <summary> | /// <summary> | ||||
/// Gets when the tag was created. | /// Gets when the tag was created. | ||||
/// </summary> | /// </summary> | ||||
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id); | 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) | if (emojiId.HasValue && emojiId.Value != 0) | ||||
Emoji = new Emote(emojiId.Value, emojiName, false); | |||||
Emoji = new Emote(emojiId.Value, null, false); | |||||
else if (emojiName != null) | else if (emojiName != null) | ||||
Emoji = new Emoji(name); | Emoji = new Emoji(name); | ||||
else | else | ||||
@@ -48,7 +54,7 @@ namespace Discord | |||||
Id = id; | Id = id; | ||||
Name = name; | Name = name; | ||||
Moderated = moderated; | |||||
IsModerated = moderated; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,128 @@ | |||||
#nullable enable | |||||
using System; | |||||
namespace Discord; | |||||
public class ForumTagBuilder | |||||
{ | |||||
private string? _name; | |||||
private IEmote? _emoji; | |||||
private bool _moderated; | |||||
/// <summary> | |||||
/// Returns the maximum length of name allowed by Discord. | |||||
/// </summary> | |||||
public const int MaxNameLength = 20; | |||||
/// <summary> | |||||
/// Gets or sets the name of the tag. | |||||
/// </summary> | |||||
/// <exception cref="ArgumentException">Name length must be less than or equal to <see cref="MaxNameLength"/>.</exception> | |||||
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; | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Gets or sets the emoji of the tag. | |||||
/// </summary> | |||||
public IEmote? Emoji | |||||
{ | |||||
get { return _emoji; } | |||||
set { _emoji = value; } | |||||
} | |||||
/// <summary> | |||||
/// Gets or sets whether this tag can only be added to or removed from threads by a member | |||||
/// with the <see cref="GuildPermissions.ManageThreads"/> permission | |||||
/// </summary> | |||||
public bool IsModerated | |||||
{ | |||||
get { return _moderated; } | |||||
set { _moderated = value; } | |||||
} | |||||
/// <summary> | |||||
/// Initializes a new <see cref="ForumTagBuilder"/> class. | |||||
/// </summary> | |||||
public ForumTagBuilder() | |||||
{ | |||||
} | |||||
/// <summary> | |||||
/// Initializes a new <see cref="ForumTagBuilder"/> class with values | |||||
/// </summary> | |||||
public ForumTagBuilder(string name) | |||||
{ | |||||
Name = name; | |||||
IsModerated = false; | |||||
} | |||||
/// <summary> | |||||
/// Initializes a new <see cref="ForumTagBuilder"/> class with values | |||||
/// </summary> | |||||
public ForumTagBuilder(string name, IEmote? emoji = null, bool moderated = false) | |||||
{ | |||||
Name = name; | |||||
Emoji = emoji; | |||||
IsModerated = moderated; | |||||
} | |||||
/// <summary> | |||||
/// Initializes a new <see cref="ForumTagBuilder"/> class with values | |||||
/// </summary> | |||||
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; | |||||
} | |||||
/// <summary> | |||||
/// Builds the Tag. | |||||
/// </summary> | |||||
/// <returns>An instance of <see cref="ForumTagProperties"/></returns> | |||||
/// <exception cref="ArgumentNullException">"Name must be set to build the tag"</exception> | |||||
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); | |||||
} | |||||
/// <summary> | |||||
/// Sets the name of the tag. | |||||
/// </summary> | |||||
/// <exception cref="ArgumentException">Name length must be less than or equal to <see cref="MaxNameLength"/>.</exception> | |||||
public ForumTagBuilder WithName(string name) | |||||
{ | |||||
Name = name; | |||||
return this; | |||||
} | |||||
/// <summary> | |||||
/// Sets the emoji of the tag. | |||||
/// </summary> | |||||
public ForumTagBuilder WithEmoji(IEmote? emoji) | |||||
{ | |||||
Emoji = emoji; | |||||
return this; | |||||
} | |||||
/// <summary> | |||||
/// Sets the IsModerated of the tag. | |||||
/// </summary> | |||||
public ForumTagBuilder WithModerated(bool moderated) | |||||
{ | |||||
IsModerated = moderated; | |||||
return this; | |||||
} | |||||
} |
@@ -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); | |||||
} |
@@ -0,0 +1,29 @@ | |||||
namespace Discord; | |||||
#nullable enable | |||||
public class ForumTagProperties | |||||
{ | |||||
/// <summary> | |||||
/// Gets the name of the tag. | |||||
/// </summary> | |||||
public string Name { get; } | |||||
/// <summary> | |||||
/// Gets the emoji of the tag or <see langword="null"/> if none is set. | |||||
/// </summary> | |||||
public IEmote? Emoji { get; } | |||||
/// <summary> | |||||
/// Gets whether this tag can only be added to or removed from threads by a member | |||||
/// with the <see cref="GuildPermissions.ManageThreads"/> permission | |||||
/// </summary> | |||||
public bool IsModerated { get; } | |||||
internal ForumTagProperties(string name, IEmote? emoji = null, bool isMmoderated = false) | |||||
{ | |||||
Name = name; | |||||
Emoji = emoji; | |||||
IsModerated = isMmoderated; | |||||
} | |||||
} |
@@ -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<ulong?> EmojiId { get; set; } | |||||
[JsonProperty("emoji_name")] | |||||
public Optional<string> EmojiName { get; set; } | |||||
[JsonProperty("moderated")] | |||||
public bool Moderated { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest; | |||||
[JsonObject(MemberSerialization = MemberSerialization.OptIn)] | |||||
internal class ModifyForumChannelParams : ModifyTextChannelParams | |||||
{ | |||||
[JsonProperty("available_tags")] | |||||
public Optional<ForumTagParams[]> Tags { get; set; } | |||||
[JsonProperty("default_thread_rate_limit_per_user")] | |||||
public Optional<int> DefaultSlowModeInterval { get; set; } | |||||
[JsonProperty("rate_limit_per_user")] | |||||
public Optional<int> ThreadCreationInterval { get; set; } | |||||
} |
@@ -13,5 +13,7 @@ namespace Discord.API.Rest | |||||
public Optional<ulong?> CategoryId { get; set; } | public Optional<ulong?> CategoryId { get; set; } | ||||
[JsonProperty("permission_overwrites")] | [JsonProperty("permission_overwrites")] | ||||
public Optional<Overwrite[]> Overwrites { get; set; } | public Optional<Overwrite[]> Overwrites { get; set; } | ||||
[JsonProperty("flags")] | |||||
public Optional<ChannelFlags?> Flags { get; set; } | |||||
} | } | ||||
} | } |
@@ -22,5 +22,8 @@ namespace Discord.API.Rest | |||||
[JsonProperty("applied_tags")] | [JsonProperty("applied_tags")] | ||||
public Optional<IEnumerable<ulong>> AppliedTags { get; set; } | public Optional<IEnumerable<ulong>> AppliedTags { get; set; } | ||||
[JsonProperty("flags")] | |||||
public Optional<ChannelFlags> Flags { get; set; } | |||||
} | } | ||||
} | } |
@@ -38,6 +38,7 @@ namespace Discord.Rest | |||||
Deny = overwrite.Permissions.DenyValue.ToString() | Deny = overwrite.Permissions.DenyValue.ToString() | ||||
}).ToArray() | }).ToArray() | ||||
: Optional.Create<API.Overwrite[]>(), | : Optional.Create<API.Overwrite[]>(), | ||||
Flags = args.Flags.GetValueOrDefault(), | |||||
}; | }; | ||||
return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -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<Model> ModifyAsync(IForumChannel channel, BaseDiscordClient client, | |||||
Action<ForumChannelProperties> 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<API.Overwrite[]>(), | |||||
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<ulong?>.Unspecified, | |||||
EmojiName = tag.Emoji is Emoji emoji | |||||
? emoji.Name | |||||
: Optional<string>.Unspecified | |||||
}).ToArray() | |||||
: Optional.Create<API.ForumTagParams[]>(), | |||||
Flags = args.Flags.GetValueOrDefault(), | |||||
Topic = args.Topic, | |||||
}; | |||||
return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | |||||
} | |||||
} |
@@ -58,7 +58,8 @@ namespace Discord.Rest | |||||
AutoArchiveDuration = args.AutoArchiveDuration, | AutoArchiveDuration = args.AutoArchiveDuration, | ||||
Locked = args.Locked, | Locked = args.Locked, | ||||
Slowmode = args.SlowModeInterval, | Slowmode = args.SlowModeInterval, | ||||
AppliedTags = args.AppliedTags | |||||
AppliedTags = args.AppliedTags, | |||||
Flags = args.Flags, | |||||
}; | }; | ||||
return await client.ApiClient.ModifyThreadAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | return await client.ApiClient.ModifyThreadAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | ||||
} | } | ||||
@@ -63,6 +63,10 @@ namespace Discord.WebSocket | |||||
).ToImmutableArray(); | ).ToImmutableArray(); | ||||
} | } | ||||
/// <inheritdoc /> | |||||
public virtual Task ModifyAsync(Action<ForumChannelProperties> func, RequestOptions options = null) | |||||
=> ForumHelper.ModifyAsync(this, Discord, func, options); | |||||
/// <inheritdoc cref="IForumChannel.CreatePostAsync(string, ThreadArchiveDuration, int?, string, Embed, RequestOptions, AllowedMentions, MessageComponent, ISticker[], Embed[], MessageFlags)"/> | /// <inheritdoc cref="IForumChannel.CreatePostAsync(string, ThreadArchiveDuration, int?, string, Embed, RequestOptions, AllowedMentions, MessageComponent, ISticker[], Embed[], MessageFlags)"/> | ||||
public Task<RestThreadChannel> 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) | public Task<RestThreadChannel> 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); | => ThreadHelper.CreatePostAsync(this, Discord, title, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags); | ||||