diff --git a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
index 06ff0e6a4..4a0e2bf9b 100644
--- a/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
+++ b/src/Discord.Net.Core/Entities/Channels/IForumChannel.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -39,15 +40,129 @@ namespace Discord
///
/// The title of the post.
/// The archive duration of the post.
- ///
- /// The starting message of the post. The content of the message supports full markdown.
+ /// 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.
///
- Task CreatePostAsync(string title, ThreadArchiveDuration archiveDuration, Message message, int? slowmode = null, RequestOptions options = null);
+ 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.
diff --git a/src/Discord.Net.Core/Entities/Messages/Message.cs b/src/Discord.Net.Core/Entities/Messages/Message.cs
deleted file mode 100644
index 581228ba9..000000000
--- a/src/Discord.Net.Core/Entities/Messages/Message.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Discord
-{
- ///
- /// Represents a message created by a that can be sent to a channel.
- ///
- public sealed class Message
- {
- ///
- /// Gets the content of the message.
- ///
- public string Content { get; }
-
- ///
- /// Gets whether or not this message should be read by a text-to-speech engine.
- ///
- public bool IsTTS { get; }
-
- ///
- /// Gets a collection of embeds sent along with this message.
- ///
- public IReadOnlyCollection Embeds { get; }
-
- ///
- /// Gets the allowed mentions for this message.
- ///
- public AllowedMentions AllowedMentions { get; }
-
- ///
- /// Gets the message reference (reply to) for this message.
- ///
- public MessageReference MessageReference { get; }
-
- ///
- /// Gets the components of this message.
- ///
- public MessageComponent Components { get; }
-
- ///
- /// Gets a collection of sticker ids that will be sent with this message.
- ///
- public IReadOnlyCollection StickerIds { get; }
-
- ///
- /// Gets a collection of files sent with this message.
- ///
- public IReadOnlyCollection Attachments { get; }
-
- ///
- /// Gets the message flags for this message.
- ///
- public MessageFlags Flags { get; }
-
- internal Message(string content, bool istts, IReadOnlyCollection embeds, AllowedMentions allowedMentions,
- MessageReference messagereference, MessageComponent components, IReadOnlyCollection stickerIds,
- IReadOnlyCollection attachments, MessageFlags flags)
- {
- Content = content;
- IsTTS = istts;
- Embeds = embeds;
- AllowedMentions = allowedMentions;
- MessageReference = messagereference;
- Components = components;
- StickerIds = stickerIds;
- Attachments = attachments;
- Flags = flags;
- }
- }
-}
diff --git a/src/Discord.Net.Core/Entities/Messages/MessageBuilder.cs b/src/Discord.Net.Core/Entities/Messages/MessageBuilder.cs
deleted file mode 100644
index 738c4102b..000000000
--- a/src/Discord.Net.Core/Entities/Messages/MessageBuilder.cs
+++ /dev/null
@@ -1,220 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Discord
-{
- ///
- /// Represents a generic message builder that can build s.
- ///
- public class MessageBuilder
- {
- private string _content;
- private List _stickers;
- private List _embeds;
-
- ///
- /// Gets or sets the content of this message
- ///
- /// The content is bigger than the .
- public string Content
- {
- get => _content;
- set
- {
- if (_content?.Length > DiscordConfig.MaxMessageSize)
- throw new ArgumentOutOfRangeException(nameof(value), $"Message size must be less than or equal to {DiscordConfig.MaxMessageSize} characters");
-
- _content = value;
- }
- }
-
- ///
- /// Gets or sets whether or not this message is TTS.
- ///
- public bool IsTTS { get; set; }
-
- ///
- /// Gets or sets the embeds of this message.
- ///
- public List Embeds
- {
- get
- {
- if (_embeds == null)
- _embeds = new();
- return _embeds;
- }
- set
- {
- if (value?.Count > DiscordConfig.MaxEmbedsPerMessage)
- throw new ArgumentOutOfRangeException(nameof(value), $"Embed count must be less than or equal to {DiscordConfig.MaxEmbedsPerMessage}");
-
- _embeds = value;
- }
- }
-
- ///
- /// Gets or sets the allowed mentions of this message.
- ///
- public AllowedMentions AllowedMentions { get; set; }
-
- ///
- /// Gets or sets the message reference (reply to) of this message.
- ///
- public MessageReference MessageReference { get; set; }
-
- ///
- /// Gets or sets the components of this message.
- ///
- public ComponentBuilder Components { get; set; } = new();
-
- ///
- /// Gets or sets the stickers sent with this message.
- ///
- public List Stickers
- {
- get => _stickers;
- set
- {
- if (value?.Count > DiscordConfig.MaxStickersPerMessage)
- throw new ArgumentOutOfRangeException(nameof(value), $"Sticker count must be less than or equal to {DiscordConfig.MaxStickersPerMessage}");
-
- _stickers = value;
- }
- }
-
- ///
- /// Gets or sets the files sent with this message.
- ///
- public List Files { get; set; } = new();
-
- ///
- /// Gets or sets the message flags.
- ///
- public MessageFlags Flags { get; set; }
-
- ///
- /// Sets the of this message.
- ///
- /// The content of the message.
- /// The current builder.
- public MessageBuilder WithContent(string content)
- {
- Content = content;
- return this;
- }
-
- ///
- /// Sets the of this message.
- ///
- /// whether or not this message is tts.
- /// The current builder.
- public MessageBuilder WithTTS(bool isTTS)
- {
- IsTTS = isTTS;
- return this;
- }
-
- public MessageBuilder WithEmbeds(params EmbedBuilder[] embeds)
- {
- Embeds = new(embeds);
- return this;
- }
-
- public MessageBuilder AddEmbed(EmbedBuilder embed)
- {
- if (_embeds?.Count >= DiscordConfig.MaxEmbedsPerMessage)
- throw new ArgumentOutOfRangeException(nameof(embed.Length), $"A message can only contain a maximum of {DiscordConfig.MaxEmbedsPerMessage} embeds");
-
- _embeds ??= new();
-
- _embeds.Add(embed);
-
- return this;
- }
-
- public MessageBuilder WithAllowedMentions(AllowedMentions allowedMentions)
- {
- AllowedMentions = allowedMentions;
- return this;
- }
-
- public MessageBuilder WithMessageReference(MessageReference reference)
- {
- MessageReference = reference;
- return this;
- }
-
- public MessageBuilder WithMessageReference(IMessage message)
- {
- if (message != null)
- MessageReference = new MessageReference(message.Id, message.Channel?.Id, ((IGuildChannel)message.Channel)?.GuildId);
- return this;
- }
-
- public MessageBuilder WithComponentBuilder(ComponentBuilder builder)
- {
- Components = builder;
- return this;
- }
-
- public MessageBuilder WithButton(ButtonBuilder button, int row = 0)
- {
- Components ??= new();
- Components.WithButton(button, row);
- return this;
- }
-
- public MessageBuilder WithButton(
- string label = null,
- string customId = null,
- ButtonStyle style = ButtonStyle.Primary,
- IEmote emote = null,
- string url = null,
- bool disabled = false,
- int row = 0)
- {
- Components ??= new();
- Components.WithButton(label, customId, style, emote, url, disabled, row);
- return this;
- }
-
- public MessageBuilder WithSelectMenu(SelectMenuBuilder menu, int row = 0)
- {
- Components ??= new();
- Components.WithSelectMenu(menu, row);
- return this;
- }
-
- public MessageBuilder WithSelectMenu(string customId, List options,
- string placeholder = null, int minValues = 1, int maxValues = 1, bool disabled = false, int row = 0)
- {
- Components ??= new();
- Components.WithSelectMenu(customId, options, placeholder, minValues, maxValues, disabled, row);
- return this;
- }
-
- public Message Build()
- {
- var embeds = _embeds != null && _embeds.Count > 0
- ? _embeds.Select(x => x.Build()).ToImmutableArray()
- : ImmutableArray.Empty;
-
- return new Message(
- _content,
- IsTTS,
- embeds,
- AllowedMentions,
- MessageReference,
- Components?.Build(),
- _stickers != null && _stickers.Any() ? _stickers.Select(x => x.Id).ToImmutableArray() : ImmutableArray.Empty,
- Files?.ToImmutableArray() ?? ImmutableArray.Empty,
- Flags
- );
- }
- }
-}
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 Embeds { get; set; }
+
+ [JsonProperty("allowed_mentions")]
+ public Optional AllowedMentions { get; set; }
+
+ [JsonProperty("components")]
+ public Optional Components { get; set; }
+
+ [JsonProperty("sticker_ids")]
+ public Optional Stickers { get; set; }
+
+ [JsonProperty("flags")]
+ public Optional Flags { get; set; }
+ }
+}
diff --git a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
index 0a5c258ad..0c8bc5494 100644
--- a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs
@@ -22,7 +22,6 @@ namespace Discord.API.Rest
public Optional Content { get; set; }
- public Optional IsTTS { get; set; }
public Optional Embeds { get; set; }
public Optional AllowedMentions { get; set; }
public Optional MessageComponent { get; set; }
@@ -39,26 +38,27 @@ namespace Discord.API.Rest
var d = new Dictionary();
var payload = new Dictionary();
+ var message = new Dictionary();
- payload["title"] = Title;
+ payload["name"] = Title;
payload["auto_archive_duration"] = ArchiveDuration;
if (Slowmode.IsSpecified)
payload["rate_limit_per_user"] = Slowmode.Value;
+
+ // message
if (Content.IsSpecified)
- payload["content"] = Content.Value;
- if (IsTTS.IsSpecified)
- payload["tts"] = IsTTS.Value;
+ message["content"] = Content.Value;
if (Embeds.IsSpecified)
- payload["embeds"] = Embeds.Value;
+ message["embeds"] = Embeds.Value;
if (AllowedMentions.IsSpecified)
- payload["allowed_mentions"] = AllowedMentions.Value;
+ message["allowed_mentions"] = AllowedMentions.Value;
if (MessageComponent.IsSpecified)
- payload["components"] = MessageComponent.Value;
+ message["components"] = MessageComponent.Value;
if (Stickers.IsSpecified)
- payload["sticker_ids"] = Stickers.Value;
+ message["sticker_ids"] = Stickers.Value;
if (Flags.IsSpecified)
- payload["flags"] = Flags.Value;
+ message["flags"] = Flags.Value;
List attachments = new();
@@ -79,7 +79,9 @@ namespace Discord.API.Rest
});
}
- payload["attachments"] = attachments;
+ message["attachments"] = attachments;
+
+ payload["message"] = message;
var json = new StringBuilder();
using (var text = new StringWriter(json))
diff --git a/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs b/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
index 468610062..974e07c0a 100644
--- a/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
+++ b/src/Discord.Net.Rest/API/Rest/CreatePostParams.cs
@@ -19,26 +19,7 @@ namespace Discord.API.Rest
[JsonProperty("rate_limit_per_user")]
public Optional Slowmode { get; set; }
- // message
- [JsonProperty("content")]
- public string Content { get; set; }
-
- [JsonProperty("tts")]
- public Optional IsTTS { get; set; }
-
- [JsonProperty("embeds")]
- public Optional Embeds { get; set; }
-
- [JsonProperty("allowed_mentions")]
- public Optional AllowedMentions { get; set; }
-
- [JsonProperty("components")]
- public Optional Components { get; set; }
-
- [JsonProperty("sticker_ids")]
- public Optional Stickers { get; set; }
-
- [JsonProperty("flags")]
- public Optional Flags { get; set; }
+ [JsonProperty("message")]
+ public ForumThreadMessage Message { get; set; }
}
}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestForumChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestForumChannel.cs
new file mode 100644
index 000000000..aff8400aa
--- /dev/null
+++ b/src/Discord.Net.Rest/Entities/Channels/RestForumChannel.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Model = Discord.API.Channel;
+
+namespace Discord.Rest
+{
+ ///
+ /// Represents a REST-based forum channel in a guild.
+ ///
+ public class RestForumChannel : RestGuildChannel, IForumChannel
+ {
+ ///
+ public bool IsNsfw { get; private set; }
+
+ ///
+ public string Topic { get; private set; }
+
+ ///
+ public ThreadArchiveDuration DefaultAutoArchiveDuration { get; private set; }
+
+ ///
+ public IReadOnlyCollection Tags { get; private set; }
+
+ ///
+ public string Mention => MentionUtils.MentionChannel(Id);
+
+ internal RestForumChannel(BaseDiscordClient client, IGuild guild, ulong id)
+ : base(client, guild, id)
+ {
+
+ }
+
+ internal new static RestStageChannel Create(BaseDiscordClient discord, IGuild guild, Model model)
+ {
+ var entity = new RestStageChannel(discord, guild, model.Id);
+ entity.Update(model);
+ return entity;
+ }
+
+ internal override void Update(Model model)
+ {
+ base.Update(model);
+ IsNsfw = model.Nsfw.GetValueOrDefault(false);
+ Topic = model.Topic.GetValueOrDefault();
+ DefaultAutoArchiveDuration = model.AutoArchiveDuration.GetValueOrDefault(ThreadArchiveDuration.OneDay);
+
+ Tags = model.ForumTags.GetValueOrDefault(Array.Empty()).Select(
+ x => new ForumTag(x.Id, x.Name, x.EmojiId.GetValueOrDefault(null), x.EmojiName.GetValueOrDefault())
+ ).ToImmutableArray();
+ }
+
+ ///
+ 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);
+
+ ///
+ public async 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)
+ {
+ using var file = new FileAttachment(filePath, isSpoiler: isSpoiler);
+ return await ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { file }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ }
+
+ ///
+ public async 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)
+ {
+ using var file = new FileAttachment(stream, filename, isSpoiler: isSpoiler);
+ return await ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { file }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ }
+
+ ///
+ 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)
+ => ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { attachment }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
+
+ ///
+ 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)
+ => ThreadHelper.CreatePostAsync(this, Discord, title, attachments, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
+
+ ///
+ public Task> GetActiveThreadsAsync(RequestOptions options = null)
+ => ThreadHelper.GetActiveThreadsAsync(Guild, Discord, options);
+
+ ///
+ public Task> GetJoinedPrivateArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null)
+ => ThreadHelper.GetJoinedPrivateArchivedThreadsAsync(this, Discord, limit, before, options);
+
+ ///
+ public Task> GetPrivateArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null)
+ => ThreadHelper.GetPrivateArchivedThreadsAsync(this, Discord, limit, before, options);
+
+ ///
+ public Task> GetPublicArchivedThreadsAsync(int? limit = null, DateTimeOffset? before = null, RequestOptions options = null)
+ => ThreadHelper.GetPublicArchivedThreadsAsync(this, Discord, limit, before, options);
+
+ #region IForumChannel
+ async Task> IForumChannel.GetActiveThreadsAsync(RequestOptions options)
+ => await GetActiveThreadsAsync(options).ConfigureAwait(false);
+ async Task> IForumChannel.GetPublicArchivedThreadsAsync(int? limit, DateTimeOffset? before, RequestOptions options)
+ => await GetPublicArchivedThreadsAsync(limit, before, options).ConfigureAwait(false);
+ async Task> IForumChannel.GetPrivateArchivedThreadsAsync(int? limit, DateTimeOffset? before, RequestOptions options)
+ => await GetPrivateArchivedThreadsAsync(limit, before, options).ConfigureAwait(false);
+ async Task> IForumChannel.GetJoinedPrivateArchivedThreadsAsync(int? limit, DateTimeOffset? before, RequestOptions options)
+ => await GetJoinedPrivateArchivedThreadsAsync(limit, before, options).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostAsync(string title, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostAsync(title, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, string filePath, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, filePath, archiveDuration, slowmode, text, embed, options, isSpoiler, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, Stream stream, string filename, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, stream, filename, archiveDuration, slowmode, text, embed, options, isSpoiler, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, FileAttachment attachment, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, attachment, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFilesAsync(string title, IEnumerable attachments, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFilesAsync(title, attachments, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
+
+ #endregion
+ }
+}
diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
index fa2362854..4f9af0335 100644
--- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
@@ -39,6 +39,7 @@ namespace Discord.Rest
ChannelType.Text => RestTextChannel.Create(discord, guild, model),
ChannelType.Voice => RestVoiceChannel.Create(discord, guild, model),
ChannelType.Stage => RestStageChannel.Create(discord, guild, model),
+ ChannelType.Forum => RestForumChannel.Create(discord, guild, model),
ChannelType.Category => RestCategoryChannel.Create(discord, guild, model),
ChannelType.PublicThread or ChannelType.PrivateThread or ChannelType.NewsThread => RestThreadChannel.Create(discord, guild, model),
_ => new RestGuildChannel(discord, guild, model.Id),
diff --git a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
index 95c0496bf..f5fce5a50 100644
--- a/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
+++ b/src/Discord.Net.Rest/Entities/Channels/ThreadHelper.cs
@@ -103,47 +103,112 @@ namespace Discord.Rest
return RestThreadUser.Create(client, channel.Guild, model, channel);
}
- public static async Task CreatePostAsync(IForumChannel channel, BaseDiscordClient client, string title, ThreadArchiveDuration archiveDuration, Message message, int? slowmode = null, RequestOptions options = null)
+ public static async Task CreatePostAsync(IForumChannel channel, BaseDiscordClient client, 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)
{
- Model model;
+ embeds ??= Array.Empty();
+ if (embed != null)
+ embeds = new[] { embed }.Concat(embeds).ToArray();
+
+ Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
+ Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
+ Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
- if (message.Attachments?.Any() ?? false)
+ // check that user flag and user Id list are exclusive, same with role flag and role Id list
+ if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
{
- var args = new CreateMultipartPostAsync(message.Attachments.ToArray())
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
+ allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
{
- AllowedMentions = message.AllowedMentions.ToModel(),
- ArchiveDuration = archiveDuration,
- Content = message.Content,
- Embeds = message.Embeds.Any() ? message.Embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified,
- Flags = message.Flags,
- IsTTS = message.IsTTS,
- MessageComponent = message.Components?.Components?.Any() ?? false ? message.Components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified,
- Slowmode = slowmode,
- Stickers = message.StickerIds?.Any() ?? false ? message.StickerIds.ToArray() : Optional.Unspecified,
- Title = title
- };
-
- model = await client.ApiClient.CreatePostAsync(channel.Id, args, options).ConfigureAwait(false);
+ throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
+ }
+
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
+ allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+ {
+ throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
+ }
}
- else
+
+ if (stickers != null)
{
- var args = new CreatePostParams()
+ Preconditions.AtMost(stickers.Length, 3, nameof(stickers), "A max of 3 stickers are allowed.");
+ }
+
+
+ if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds)
+ throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds and none.", nameof(flags));
+
+ var args = new CreatePostParams()
+ {
+ Title = title,
+ ArchiveDuration = archiveDuration,
+ Slowmode = slowmode,
+ Message = new()
{
- AllowedMentions = message.AllowedMentions.ToModel(),
- ArchiveDuration = archiveDuration,
- Content = message.Content,
- Embeds = message.Embeds.Any() ? message.Embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified,
- Flags = message.Flags,
- IsTTS = message.IsTTS,
- Components = message.Components?.Components?.Any() ?? false ? message.Components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified,
- Slowmode = slowmode,
- Stickers = message.StickerIds?.Any() ?? false ? message.StickerIds.ToArray() : Optional.Unspecified,
- Title = title
- };
-
- model = await client.ApiClient.CreatePostAsync(channel.Id, args, options);
+ AllowedMentions = allowedMentions.ToModel(),
+ Content = text,
+ Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified,
+ Flags = flags,
+ Components = components?.Components?.Any() ?? false ? components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified,
+ Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional.Unspecified,
+ }
+ };
+
+ var model = await client.ApiClient.CreatePostAsync(channel.Id, args, options).ConfigureAwait(false);
+
+ return RestThreadChannel.Create(client, channel.Guild, model);
+ }
+
+ public static async Task CreatePostAsync(IForumChannel channel, BaseDiscordClient client, string title, IEnumerable attachments, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ {
+ embeds ??= Array.Empty();
+ if (embed != null)
+ embeds = new[] { embed }.Concat(embeds).ToArray();
+
+ Preconditions.AtMost(allowedMentions?.RoleIds?.Count ?? 0, 100, nameof(allowedMentions.RoleIds), "A max of 100 role Ids are allowed.");
+ Preconditions.AtMost(allowedMentions?.UserIds?.Count ?? 0, 100, nameof(allowedMentions.UserIds), "A max of 100 user Ids are allowed.");
+ Preconditions.AtMost(embeds.Length, 10, nameof(embeds), "A max of 10 embeds are allowed.");
+
+ // check that user flag and user Id list are exclusive, same with role flag and role Id list
+ if (allowedMentions != null && allowedMentions.AllowedTypes.HasValue)
+ {
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Users) &&
+ allowedMentions.UserIds != null && allowedMentions.UserIds.Count > 0)
+ {
+ throw new ArgumentException("The Users flag is mutually exclusive with the list of User Ids.", nameof(allowedMentions));
+ }
+
+ if (allowedMentions.AllowedTypes.Value.HasFlag(AllowedMentionTypes.Roles) &&
+ allowedMentions.RoleIds != null && allowedMentions.RoleIds.Count > 0)
+ {
+ throw new ArgumentException("The Roles flag is mutually exclusive with the list of Role Ids.", nameof(allowedMentions));
+ }
+ }
+
+ if (stickers != null)
+ {
+ Preconditions.AtMost(stickers.Length, 3, nameof(stickers), "A max of 3 stickers are allowed.");
}
+
+ if (flags is not MessageFlags.None and not MessageFlags.SuppressEmbeds)
+ throw new ArgumentException("The only valid MessageFlags are SuppressEmbeds and none.", nameof(flags));
+
+ var args = new CreateMultipartPostAsync(attachments.ToArray())
+ {
+ AllowedMentions = allowedMentions.ToModel(),
+ ArchiveDuration = archiveDuration,
+ Content = text,
+ Embeds = embeds.Any() ? embeds.Select(x => x.ToModel()).ToArray() : Optional.Unspecified,
+ Flags = flags,
+ MessageComponent = components?.Components?.Any() ?? false ? components.Components.Select(x => new API.ActionRowComponent(x)).ToArray() : Optional.Unspecified,
+ Slowmode = slowmode,
+ Stickers = stickers?.Any() ?? false ? stickers.Select(x => x.Id).ToArray() : Optional.Unspecified,
+ Title = title
+ };
+
+ var model = await client.ApiClient.CreatePostAsync(channel.Id, args, options);
+
return RestThreadChannel.Create(client, channel.Guild, model);
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
index 5cb1ff5e4..bc6e28442 100644
--- a/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
+++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketForumChannel.cs
@@ -2,6 +2,7 @@ using Discord.Rest;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -45,14 +46,46 @@ namespace Discord.WebSocket
Topic = model.Topic.GetValueOrDefault();
DefaultAutoArchiveDuration = model.AutoArchiveDuration.GetValueOrDefault(ThreadArchiveDuration.OneDay);
- Tags = model.ForumTags.GetValueOrDefault(new API.ForumTags[0]).Select(
+ Tags = model.ForumTags.GetValueOrDefault(Array.Empty()).Select(
x => new ForumTag(x.Id, x.Name, x.EmojiId.GetValueOrDefault(null), x.EmojiName.GetValueOrDefault())
).ToImmutableArray();
}
- ///
- public Task CreatePostAsync(string title, ThreadArchiveDuration archiveDuration, Message message, int? slowmode = null, RequestOptions options = null)
- => ThreadHelper.CreatePostAsync(this, Discord, title, archiveDuration, message, slowmode, 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);
+
+ ///
+ public async 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)
+ {
+ using var file = new FileAttachment(filePath, isSpoiler: isSpoiler);
+ return await ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { file }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ }
+
+ ///
+ public async 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)
+ {
+ using var file = new FileAttachment(stream, filename, isSpoiler: isSpoiler);
+ return await ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { file }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ }
+
+ ///
+ 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)
+ => ThreadHelper.CreatePostAsync(this, Discord, title, new FileAttachment[] { attachment }, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
+
+ ///
+ 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)
+ => ThreadHelper.CreatePostAsync(this, Discord, title, attachments, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
///
public Task> GetActiveThreadsAsync(RequestOptions options = null)
@@ -71,8 +104,6 @@ namespace Discord.WebSocket
=> ThreadHelper.GetPublicArchivedThreadsAsync(this, Discord, limit, before, options);
#region IForumChannel
- async Task IForumChannel.CreatePostAsync(string title, ThreadArchiveDuration archiveDuration, Message message, int? slowmode, RequestOptions options)
- => await CreatePostAsync(title, archiveDuration, message, slowmode, options).ConfigureAwait(false);
async Task> IForumChannel.GetActiveThreadsAsync(RequestOptions options)
=> await GetActiveThreadsAsync(options).ConfigureAwait(false);
async Task> IForumChannel.GetPublicArchivedThreadsAsync(int? limit, DateTimeOffset? before, RequestOptions options)
@@ -81,6 +112,17 @@ namespace Discord.WebSocket
=> await GetPrivateArchivedThreadsAsync(limit, before, options).ConfigureAwait(false);
async Task> IForumChannel.GetJoinedPrivateArchivedThreadsAsync(int? limit, DateTimeOffset? before, RequestOptions options)
=> await GetJoinedPrivateArchivedThreadsAsync(limit, before, options).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostAsync(string title, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostAsync(title, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, string filePath, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, filePath, archiveDuration, slowmode, text, embed, options, isSpoiler, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, Stream stream, string filename, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, bool isSpoiler, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, stream, filename, archiveDuration, slowmode, text, embed, options, isSpoiler, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFileAsync(string title, FileAttachment attachment, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFileAsync(title, attachment, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags).ConfigureAwait(false);
+ async Task IForumChannel.CreatePostWithFilesAsync(string title, IEnumerable attachments, ThreadArchiveDuration archiveDuration, int? slowmode, string text, Embed embed, RequestOptions options, AllowedMentions allowedMentions, MessageComponent components, ISticker[] stickers, Embed[] embeds, MessageFlags flags)
+ => await CreatePostWithFilesAsync(title, attachments, archiveDuration, slowmode, text, embed, options, allowedMentions, components, stickers, embeds, flags);
+
#endregion
}
}
diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
index e12f3d1ef..9ce2f507a 100644
--- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
+++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
@@ -705,7 +705,15 @@ namespace Discord.WebSocket
///
public SocketThreadChannel GetThreadChannel(ulong id)
=> GetChannel(id) as SocketThreadChannel;
-
+ ///
+ /// Gets a forum channel in this guild.
+ ///
+ /// The snowflake identifier for the forum channel.
+ ///
+ /// A forum channel associated with the specified ; if none is found.
+ ///
+ public SocketForumChannel GetForumChannel(ulong id)
+ => GetChannel(id) as SocketForumChannel;
///
/// Gets a voice channel in this guild.
///