From 7a07fd62e41bb62026bfac3bf7cc6697b7e77db2 Mon Sep 17 00:00:00 2001
From: Quin Lynch <49576606+quinchs@users.noreply.github.com>
Date: Tue, 24 May 2022 02:30:25 -0300
Subject: [PATCH] feature: Forum channels (#2316)
* initial implementation
* Update SocketForumChannel.cs
* rest forum channel and remove message builder for 4.x
* Update src/Discord.Net.Core/DiscordConfig.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
* Update src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
* Update src/Discord.Net.Core/DiscordConfig.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
* Update src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
* Update src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
* Update src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>
---
src/Discord.Net.Core/DiscordConfig.cs | 10 +
.../Entities/Channels/ChannelType.cs | 4 +-
.../Entities/Channels/IForumChannel.cs | 216 ++++++++++++++++++
src/Discord.Net.Core/Entities/ForumTag.cs | 42 ++++
src/Discord.Net.Rest/API/Common/Channel.cs | 4 +
.../API/Common/ChannelThreads.cs | 3 -
src/Discord.Net.Rest/API/Common/ForumTags.cs | 21 ++
.../API/Common/ForumThreadMessage.cs | 33 +++
.../API/Rest/CreateMultipartPostAsync.cs | 96 ++++++++
.../API/Rest/CreatePostParams.cs | 25 ++
.../API/Rest/UploadFileParams.cs | 2 +-
.../API/Rest/UploadInteractionFileParams.cs | 2 +-
.../API/Rest/UploadWebhookFileParams.cs | 2 +-
src/Discord.Net.Rest/DiscordRestApiClient.cs | 26 ++-
.../Entities/Channels/RestForumChannel.cs | 131 +++++++++++
.../Entities/Channels/RestGuildChannel.cs | 1 +
.../Entities/Channels/ThreadHelper.cs | 138 +++++++++++
.../Interactions/InteractionHelper.cs | 4 +-
.../Net/Converters/UInt64Converter.cs | 4 +-
.../Entities/Channels/SocketForumChannel.cs | 128 +++++++++++
.../Entities/Channels/SocketGuildChannel.cs | 1 +
.../Entities/Guilds/SocketGuild.cs | 10 +-
22 files changed, 887 insertions(+), 16 deletions(-)
create mode 100644 src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
create mode 100644 src/Discord.Net.Core/Entities/ForumTag.cs
create mode 100644 src/Discord.Net.Rest/API/Common/ForumTags.cs
create mode 100644 src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
create mode 100644 src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
create mode 100644 src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
create mode 100644 src/Discord.Net.Rest/Entities/Channels/RestForumChannel.cs
create mode 100644 src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs
index 067c55225..2db802f1e 100644
--- a/src/Discord.Net.Core/DiscordConfig.cs
+++ b/src/Discord.Net.Core/DiscordConfig.cs
@@ -132,6 +132,16 @@ namespace Discord
///
public const int MaxAuditLogEntriesPerBatch = 100;
+ ///
+ /// Returns the max number of stickers that can be sent with a message.
+ ///
+ public const int MaxStickersPerMessage = 3;
+
+ ///
+ /// Returns the max number of embeds that can be sent with a message.
+ ///
+ public const int MaxEmbedsPerMessage = 10;
+
///
/// Gets or sets how a request should act in the case of an error, by default.
///
diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
index e60bd5031..15965abc3 100644
--- a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
+++ b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs
@@ -26,6 +26,8 @@ namespace Discord
/// The channel is a stage voice channel.
Stage = 13,
/// The channel is a guild directory used in hub servers. (Unreleased)
- GuildDirectory = 14
+ GuildDirectory = 14,
+ /// The channel is a forum channel containing multiple threads.
+ Forum = 15
}
}
diff --git a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
new file mode 100644
index 000000000..f4c6da2e2
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord
+{
+ public interface IForumChannel : IGuildChannel, IMentionable
+ {
+ ///
+ /// Gets a value that indicates whether the channel is NSFW.
+ ///
+ ///
+ /// true if the channel has the NSFW flag enabled; otherwise false.
+ ///
+ bool IsNsfw { get; }
+
+ ///
+ /// Gets the current topic for this text channel.
+ ///
+ ///
+ /// A string representing the topic set in the channel; null if none is set.
+ ///
+ string Topic { get; }
+
+ ///
+ /// Gets the default archive duration for a newly created post.
+ ///
+ ThreadArchiveDuration DefaultAutoArchiveDuration { get; }
+
+ ///
+ /// Gets a collection of tags inside of this forum channel.
+ ///
+ IReadOnlyCollection Tags { get; }
+
+ ///
+ /// Creates a new post (thread) within the forum.
+ ///
+ /// The title of the post.
+ /// The archive duration of the post.
+ /// The slowmode for the posts thread.
+ /// The message to be sent.
+ /// The to be sent.
+ /// The options to be used when sending the request.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// The message components to be included with this message. Used for interactions.
+ /// 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.
+ ///
+ /// 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);
+
+ ///
+ /// Creates a new post (thread) within the forum.
+ ///
+ /// The title of the post.
+ /// The archive duration of the post.
+ /// The slowmode for the posts thread.
+ /// The file path of the file.
+ /// The message to be sent.
+ /// The to be sent.
+ /// The options to be used when sending the request.
+ /// Whether the message attachment should be hidden as a spoiler.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// 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.
+ /// A message flag to be applied to the sent message, only is permitted.
+ ///
+ /// 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);
+
+ ///
+ /// Creates a new post (thread) within the forum.
+ ///
+ /// The title of the post.
+ /// The of the file to be sent.
+ /// The name of the attachment.
+ /// The archive duration of the post.
+ /// The slowmode for the posts thread.
+ /// The message to be sent.
+ /// The to be sent.
+ /// The options to be used when sending the request.
+ /// Whether the message attachment should be hidden as a spoiler.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// 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.
+ /// A message flag to be applied to the sent message, only is permitted.
+ ///
+ /// 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);
+
+ ///
+ /// Creates a new post (thread) within the forum.
+ ///
+ /// The title of the post.
+ /// The attachment containing the file and description.
+ /// The archive duration of the post.
+ /// The slowmode for the posts thread.
+ /// The message to be sent.
+ /// The to be sent.
+ /// The options to be used when sending the request.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// 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.
+ /// A message flag to be applied to the sent message, only is permitted.
+ ///
+ /// 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);
+
+ ///
+ /// Creates a new post (thread) within the forum.
+ ///
+ /// The title of the post.
+ /// A collection of attachments to upload.
+ /// The archive duration of the post.
+ /// The slowmode for the posts thread.
+ /// The message to be sent.
+ /// The to be sent.
+ /// The options to be used when sending the request.
+ ///
+ /// Specifies if notifications are sent for mentioned users and roles in the message .
+ /// If null, all mentioned roles and users will be notified.
+ ///
+ /// 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.
+ /// A message flag to be applied to the sent message, only is permitted.
+ ///
+ /// 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);
+
+ ///
+ /// Gets a collection of active threads within this forum channel.
+ ///
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous get operation for retrieving the threads. The task result contains
+ /// a collection of active threads.
+ ///
+ Task> GetActiveThreadsAsync(RequestOptions options = null);
+
+ ///
+ /// Gets a collection of publicly archived threads within this forum channel.
+ ///
+ /// The optional limit of how many to get.
+ /// The optional date to return threads created before this timestamp.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous get operation for retrieving the threads. The task result contains
+ /// a collection of publicly archived threads.
+ ///
+ Task> GetPublicArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null);
+
+ ///
+ /// Gets a collection of privately archived threads within this forum channel.
+ ///
+ ///
+ /// The bot requires the permission in order to execute this request.
+ ///
+ /// The optional limit of how many to get.
+ /// The optional date to return threads created before this timestamp.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous get operation for retrieving the threads. The task result contains
+ /// a collection of privately archived threads.
+ ///
+ Task> GetPrivateArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null);
+
+ ///
+ /// Gets a collection of privately archived threads that the current bot has joined within this forum channel.
+ ///
+ /// The optional limit of how many to get.
+ /// The optional date to return threads created before this timestamp.
+ /// The options to be used when sending the request.
+ ///
+ /// A task that represents an asynchronous get operation for retrieving the threads. The task result contains
+ /// a collection of privately archived threads.
+ ///
+ Task> GetJoinedPrivateArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null);
+ }
+}
diff --git a/src/Discord.Net.Core/Entities/ForumTag.cs b/src/Discord.Net.Core/Entities/ForumTag.cs
new file mode 100644
index 000000000..26ae4301e
--- /dev/null
+++ b/src/Discord.Net.Core/Entities/ForumTag.cs
@@ -0,0 +1,42 @@
+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.Rest/API/Common/Channel.cs b/src/Discord.Net.Rest/API/Common/Channel.cs
index 0eab65686..d9d7d469c 100644
--- a/src/Discord.Net.Rest/API/Common/Channel.cs
+++ b/src/Discord.Net.Rest/API/Common/Channel.cs
@@ -67,6 +67,10 @@ namespace Discord.API
[JsonProperty("member_count")]
public Optional MemberCount { get; set; }
+ //ForumChannel
+ [JsonProperty("available_tags")]
+ public Optional ForumTags { get; set; }
+
[JsonProperty("default_auto_archive_duration")]
public Optional AutoArchiveDuration { get; set; }
}
diff --git a/src/Discord.Net.Rest/API/Common/ChannelThreads.cs b/src/Discord.Net.Rest/API/Common/ChannelThreads.cs
index 94b2396bf..9fa3e38ce 100644
--- a/src/Discord.Net.Rest/API/Common/ChannelThreads.cs
+++ b/src/Discord.Net.Rest/API/Common/ChannelThreads.cs
@@ -9,8 +9,5 @@ namespace Discord.API.Rest
[JsonProperty("members")]
public ThreadMember[] Members { get; set; }
-
- [JsonProperty("has_more")]
- public bool HasMore { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/API/Common/ForumTags.cs b/src/Discord.Net.Rest/API/Common/ForumTags.cs
new file mode 100644
index 000000000..18354e7b2
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/ForumTags.cs
@@ -0,0 +1,21 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.API
+{
+ internal class ForumTags
+ {
+ [JsonProperty("id")]
+ public ulong Id { get; set; }
+ [JsonProperty("name")]
+ public string Name { get; set; }
+ [JsonProperty("emoji_id")]
+ public Optional EmojiId { get; set; }
+ [JsonProperty("emoji_name")]
+ public Optional EmojiName { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs b/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
new file mode 100644
index 000000000..132e38e5f
--- /dev/null
+++ b/src/Discord.Net.Rest/API/Common/ForumThreadMessage.cs
@@ -0,0 +1,33 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Discord.API
+{
+ internal class ForumThreadMessage
+ {
+ [JsonProperty("content")]
+ public Optional Content { get; set; }
+
+ [JsonProperty("nonce")]
+ public Optional Nonce { get; set; }
+
+ [JsonProperty("embeds")]
+ public Optional