diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs b/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs index 37f34a90ed..13d0db9878 100644 --- a/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs +++ b/src/Discord.Net.Core/Entities/Channels/ChannelFlags.cs @@ -16,7 +16,12 @@ public enum ChannelFlags Pinned = 1 << 1, /// - /// Flag given to a forum channel that requires people to select tags when posting. + /// Flag given to a forum or media channel that requires people to select tags when posting. /// - RequireTag = 1 << 4 + RequireTag = 1 << 4, + + /// + /// Flag given to a media channel that hides the embedded media download options. + /// + HideMediaDownloadOption = 1 << 15, } diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs index 15965abc31..bc39a564a9 100644 --- a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs +++ b/src/Discord.Net.Core/Entities/Channels/ChannelType.cs @@ -1,33 +1,75 @@ -namespace Discord +namespace Discord; + +/// Defines the types of channels. +public enum ChannelType { - /// Defines the types of channels. - public enum ChannelType - { - /// The channel is a text channel. - Text = 0, - /// The channel is a Direct Message channel. - DM = 1, - /// The channel is a voice channel. - Voice = 2, - /// The channel is a group channel. - Group = 3, - /// The channel is a category channel. - Category = 4, - /// The channel is a news channel. - News = 5, - /// The channel is a store channel. - Store = 6, - /// The channel is a temporary thread channel under a news channel. - NewsThread = 10, - /// The channel is a temporary thread channel under a text channel. - PublicThread = 11, - /// The channel is a private temporary thread channel under a text channel. - PrivateThread = 12, - /// The channel is a stage voice channel. - Stage = 13, - /// The channel is a guild directory used in hub servers. (Unreleased) - GuildDirectory = 14, - /// The channel is a forum channel containing multiple threads. - Forum = 15 - } + /// + /// The channel is a text channel. + /// + Text = 0, + + /// + /// The channel is a Direct Message channel. + /// + DM = 1, + + /// + /// The channel is a voice channel. + /// + Voice = 2, + + /// + /// The channel is a group channel. + /// + Group = 3, + + /// + /// The channel is a category channel. + /// + Category = 4, + + /// + /// The channel is a news channel. + /// + News = 5, + + /// + /// The channel is a store channel. + /// + Store = 6, + + /// + /// The channel is a temporary thread channel under a news channel. + /// + NewsThread = 10, + + /// + /// The channel is a temporary thread channel under a text channel. + /// + PublicThread = 11, + + /// + /// The channel is a private temporary thread channel under a text channel. + /// + PrivateThread = 12, + + /// + /// The channel is a stage voice channel. + /// + Stage = 13, + + /// + /// The channel is a guild directory used in hub servers. (Unreleased) + /// + GuildDirectory = 14, + + /// + /// The channel is a forum channel containing multiple threads. + /// + Forum = 15, + + /// + /// The channel is a media channel containing multiple threads. + /// + Media = 16, } diff --git a/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs index eb18d392c6..336e17eb0b 100644 --- a/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs +++ b/src/Discord.Net.Core/Entities/Channels/ForumChannelProperties.cs @@ -29,22 +29,25 @@ public class ForumChannelProperties : TextChannelProperties public Optional ThreadCreationInterval { get; set; } /// - /// Gets or sets a collection of tags inside of this forum channel. + /// Gets or sets a collection of tags inside of this forum channel. /// public Optional> Tags { get; set; } /// - /// Gets or sets a new default reaction emoji in this forum channel. + /// Gets or sets a new default reaction emoji in this forum channel. /// public Optional DefaultReactionEmoji { get; set; } /// - /// Gets or sets the rule used to order posts in forum channels. + /// Gets or sets the rule used to order posts in forum channels. /// public Optional DefaultSortOrder { get; set; } /// - /// Gets or sets the rule used to display posts in a forum channel. + /// Gets or sets the rule used to display posts in a forum channel. /// + /// + /// This property cannot be changed in media channels. + /// public Optional DefaultLayout { get; set; } } diff --git a/src/Discord.Net.Core/Entities/Channels/IMediaChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMediaChannel.cs new file mode 100644 index 0000000000..93cd65aab9 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/IMediaChannel.cs @@ -0,0 +1,9 @@ +namespace Discord; + +/// +/// Represents a media channel in a guild that can create posts. +/// +public interface IMediaChannel : IForumChannel +{ + +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 086a92d3dc..6e62e59cd2 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -733,6 +733,52 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// Task> GetThreadChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// + /// Gets a forum channel in this guild. + /// + /// The snowflake identifier for the stage channel. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains the stage channel associated + /// with the specified ; if none is found. + /// + Task GetForumChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + + /// + /// Gets a collection of all forum channels in this guild. + /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains a read-only collection of + /// forum channels found within this guild. + /// + Task> GetForumChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + + /// + /// Gets a forum channel in this guild. + /// + /// The snowflake identifier for the stage channel. + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains the stage channel associated + /// with the specified ; if none is found. + /// + Task GetMediaChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + + /// + /// Gets a collection of all forum channels in this guild. + /// + /// The that determines whether the object should be fetched from cache. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains a read-only collection of + /// media channels found within this guild. + /// + Task> GetMediaChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// /// Creates a new text channel in this guild. /// @@ -795,6 +841,18 @@ public interface IGuild : IDeletable, ISnowflakeEntity /// Task CreateForumChannelAsync(string name, Action func = null, RequestOptions options = null); + /// + /// Creates a new media channel in this guild. + /// + /// The new name for the media channel. + /// The delegate containing the properties to be applied to the channel upon its creation. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the newly created + /// forum channel. + /// + Task CreateMediaChannelAsync(string name, Action func = null, RequestOptions options = null); + /// /// Gets a collection of all the voice regions this guild can access. /// diff --git a/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs b/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs index b7b1cb36a4..7d7f6a4630 100644 --- a/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs +++ b/src/Discord.Net.Core/Entities/Messages/FileAttachment.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Discord { @@ -26,6 +22,11 @@ public struct FileAttachment : IDisposable /// public bool IsSpoiler { get; set; } + /// + /// Gets or sets if this file should be a thumbnail for a media channel post. + /// + public bool IsThumbnail { get; set; } + #pragma warning disable IDISP008 /// /// Gets the stream containing the file content. @@ -42,12 +43,14 @@ public struct FileAttachment : IDisposable /// The name of the attachment. /// The description of the attachment. /// Whether or not the attachment is a spoiler. - public FileAttachment(Stream stream, string fileName, string description = null, bool isSpoiler = false) + /// Whether or not this attachment should be a thumbnail for a media channel post. + public FileAttachment(Stream stream, string fileName, string description = null, bool isSpoiler = false, bool isThumbnail = false) { _isDisposed = false; FileName = fileName; Description = description; Stream = stream; + IsThumbnail = isThumbnail; try { Stream.Position = 0; @@ -67,6 +70,7 @@ public FileAttachment(Stream stream, string fileName, string description = null, /// The name of the attachment. /// The description of the attachment. /// Whether or not the attachment is a spoiler. + /// Whether or not this attachment should be a thumbnail for a media channel post. /// /// is a zero-length string, contains only white space, or contains one or more invalid /// characters as defined by . @@ -87,13 +91,14 @@ public FileAttachment(Stream stream, string fileName, string description = null, /// The file specified in was not found. /// /// An I/O error occurred while opening the file. - public FileAttachment(string path, string fileName = null, string description = null, bool isSpoiler = false) + public FileAttachment(string path, string fileName = null, string description = null, bool isSpoiler = false, bool isThumbnail = false) { _isDisposed = false; Stream = File.OpenRead(path); FileName = fileName ?? Path.GetFileName(path); Description = description; IsSpoiler = isSpoiler; + IsThumbnail = isThumbnail; } public void Dispose() diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index d33d5a4471..e317401853 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -50,6 +50,11 @@ public struct ChannelPermissions /// public static readonly ChannelPermissions Forum = new(0b01_001110_010010_110011_111101_111111_111101_010001); + /// + /// Gets a that grants all permissions for media channels. + /// + public static readonly ChannelPermissions Media = new(0b01_001110_010010_110011_111101_111111_111101_010001); + /// /// Gets a that grants all permissions for a given channel type. /// @@ -64,6 +69,7 @@ public static ChannelPermissions All(IChannel channel) ICategoryChannel _ => Category, IDMChannel _ => DM, IGroupChannel _ => Group, + IMediaChannel _ => Media, IForumChannel => Forum, _ => throw new ArgumentException(message: "Unknown channel type.", paramName: nameof(channel)), }; diff --git a/src/Discord.Net.Core/Extensions/ChannelExtensions.cs b/src/Discord.Net.Core/Extensions/ChannelExtensions.cs index a24588792c..ff9fc507c9 100644 --- a/src/Discord.Net.Core/Extensions/ChannelExtensions.cs +++ b/src/Discord.Net.Core/Extensions/ChannelExtensions.cs @@ -47,6 +47,9 @@ public static class ChannelExtensions case ITextChannel: return ChannelType.Text; + case IMediaChannel: + return ChannelType.Media; + case IForumChannel: return ChannelType.Forum; } diff --git a/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs b/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs index 4dd7645080..331d5be86d 100644 --- a/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs +++ b/src/Discord.Net.Core/Utils/ChannelTypeUtils.cs @@ -9,6 +9,6 @@ public static List AllChannelTypes() { ChannelType.Forum, ChannelType.Category, ChannelType.DM, ChannelType.Group, ChannelType.GuildDirectory, ChannelType.News, ChannelType.NewsThread, ChannelType.PrivateThread, ChannelType.PublicThread, - ChannelType.Stage, ChannelType.Store, ChannelType.Text, ChannelType.Voice + ChannelType.Stage, ChannelType.Store, ChannelType.Text, ChannelType.Voice, ChannelType.Media }; } diff --git a/src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs b/src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs index 76f135a98c..facf151bf3 100644 --- a/src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs +++ b/src/Discord.Net.Interactions/TypeConverters/SlashCommands/DefaultEntityTypeConverter.cs @@ -69,6 +69,12 @@ _ when typeof(IThreadChannel).IsAssignableFrom(type) _ when typeof(ITextChannel).IsAssignableFrom(type) => new List { ChannelType.Text }, + _ when typeof(IMediaChannel).IsAssignableFrom(type) + => new List { ChannelType.Media }, + + _ when typeof(IForumChannel).IsAssignableFrom(type) + => new List { ChannelType.Forum }, + _ => null }; } diff --git a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs index 14e5f5383f..e1cd383a50 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateMultipartPostAsync.cs @@ -78,7 +78,8 @@ public IReadOnlyDictionary ToDictionary() { id = (ulong)n, filename = filename, - description = attachment.Description ?? Optional.Unspecified + description = attachment.Description ?? Optional.Unspecified, + is_thumbnail = attachment.IsThumbnail, }); } diff --git a/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs index 5f239890cb..9a6c0aebbf 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ForumHelper.cs @@ -57,8 +57,7 @@ public static async Task ModifyAsync(IForumChannel channel, BaseDiscordCl emoji.Name : Optional.Unspecified } : Optional.Unspecified, - DefaultSortOrder = args.DefaultSortOrder, - DefaultLayout = args.DefaultLayout, + DefaultSortOrder = args.DefaultSortOrder }; return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs index 3f472c74ea..bbf52a44ef 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs @@ -50,7 +50,8 @@ ChannelType.Stage or ChannelType.NewsThread or ChannelType.PrivateThread or ChannelType.PublicThread or - ChannelType.Forum + ChannelType.Forum or + ChannelType.Media => RestGuildChannel.Create(discord, guild, model), ChannelType.DM or ChannelType.Group => CreatePrivate(discord, model) as RestChannel, ChannelType.Category => RestCategoryChannel.Create(discord, guild, model), diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 30b058476b..0f2f6e2396 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -42,6 +42,7 @@ internal static RestGuildChannel Create(BaseDiscordClient discord, IGuild guild, ChannelType.Text => RestTextChannel.Create(discord, guild, model), ChannelType.Voice => RestVoiceChannel.Create(discord, guild, model), ChannelType.Stage => RestStageChannel.Create(discord, guild, model), + ChannelType.Media => RestMediaChannel.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), diff --git a/src/Discord.Net.Rest/Entities/Channels/RestMediaChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestMediaChannel.cs new file mode 100644 index 0000000000..6126329412 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestMediaChannel.cs @@ -0,0 +1,24 @@ +using Model = Discord.API.Channel; + +namespace Discord.Rest; + +public class RestMediaChannel : RestForumChannel, IMediaChannel +{ + internal RestMediaChannel(BaseDiscordClient client, IGuild guild, ulong id) + : base(client, guild, id) + { + + } + + internal new static RestMediaChannel Create(BaseDiscordClient discord, IGuild guild, Model model) + { + var entity = new RestMediaChannel(discord, guild, model.Id); + entity.Update(model); + return entity; + } + + internal override void Update(Model model) + { + base.Update(model); + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index b88ef5adf3..65774f057c 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -428,6 +428,66 @@ public static async Task CreateForumChannelAsync(IGuild guild, var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); return RestForumChannel.Create(client, guild, model); } + + /// is . + public static async Task CreateMediaChannelAsync(IGuild guild, BaseDiscordClient client, + string name, RequestOptions options, Action func = null) + { + if (name == null) + throw new ArgumentNullException(paramName: nameof(name)); + + var props = new ForumChannelProperties(); + func?.Invoke(props); + + Preconditions.AtMost(props.Tags.IsSpecified ? props.Tags.Value.Count() : 0, 20, nameof(props.Tags), "Media channel can have max 20 tags."); + + var args = new CreateGuildChannelParams(name, ChannelType.Media) + { + Position = props.Position, + Overwrites = props.PermissionOverwrites.IsSpecified + ? props.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(), + SlowModeInterval = props.ThreadCreationInterval, + AvailableTags = props.Tags.GetValueOrDefault(Array.Empty()).Select( + x => new ModifyForumTagParams + { + Id = x.Id, + Name = x.Name, + EmojiId = x.Emoji is Emote emote + ? emote.Id + : Optional.Unspecified, + EmojiName = x.Emoji is Emoji emoji + ? emoji.Name + : Optional.Unspecified, + Moderated = x.IsModerated + }).ToArray(), + DefaultReactionEmoji = props.DefaultReactionEmoji.IsSpecified + ? new API.ModifyForumReactionEmojiParams + { + EmojiId = props.DefaultReactionEmoji.Value is Emote emote ? + emote.Id : Optional.Unspecified, + EmojiName = props.DefaultReactionEmoji.Value is Emoji emoji ? + emoji.Name : Optional.Unspecified + } + : Optional.Unspecified, + ThreadRateLimitPerUser = props.DefaultSlowModeInterval, + CategoryId = props.CategoryId, + IsNsfw = props.IsNsfw, + Topic = props.Topic, + DefaultAutoArchiveDuration = props.AutoArchiveDuration, + DefaultSortOrder = props.DefaultSortOrder.GetValueOrDefault(ForumSortOrder.LatestActivity), + }; + + var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); + return RestMediaChannel.Create(client, guild, model); + } + #endregion #region Voice Regions diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 82a0fe8029..3981814359 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -462,6 +462,64 @@ public async Task> GetTextChannelsAsync(Req return channels.OfType().ToImmutableArray(); } + /// + /// Gets a forum channel in this guild. + /// + /// The snowflake identifier for the forum channel. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains the text channel + /// associated with the specified ; if none is found. + /// + public async Task GetForumChannelAsync(ulong id, RequestOptions options = null) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false); + return channel as RestForumChannel; + } + + /// + /// Gets a collection of all forum channels in this guild. + /// + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains a read-only collection of + /// forum channels found within this guild. + /// + public async Task> GetForumChannelsAsync(RequestOptions options = null) + { + var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); + return channels.OfType().ToImmutableArray(); + } + + /// + /// Gets a media channel in this guild. + /// + /// The snowflake identifier for the text channel. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains the media channel + /// associated with the specified ; if none is found. + /// + public async Task GetMediaChannelAsync(ulong id, RequestOptions options = null) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false); + return channel as RestMediaChannel; + } + + /// + /// Gets a collection of all media channels in this guild. + /// + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous get operation. The task result contains a read-only collection of + /// media channels found within this guild. + /// + public async Task> GetMediaChannelsAsync(RequestOptions options = null) + { + var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); + return channels.OfType().ToImmutableArray(); + } + /// /// Gets a thread channel in this guild. /// @@ -735,18 +793,31 @@ public Task CreateCategoryChannelAsync(string name, Action< => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options, func); /// - /// Creates a category channel with the provided name. + /// Creates a new forum channel with the provided name. /// /// The name of the new channel. /// The delegate containing the properties to be applied to the channel upon its creation. /// The options to be used when sending the request. /// is . /// - /// The created category channel. + /// The created forum channel. /// public Task CreateForumChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateForumChannelAsync(this, Discord, name, options, func); + /// + /// Creates a new media channel in this guild. + /// + /// The new name for the media channel. + /// The delegate containing the properties to be applied to the channel upon its creation. + /// The options to be used when sending the request. + /// + /// A task that represents the asynchronous creation operation. The task result contains the newly created + /// media channel. + /// + public Task CreateMediaChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateMediaChannelAsync(this, Discord, name, options, func); + /// /// Gets a collection of all the voice regions this guild can access. /// @@ -1342,6 +1413,43 @@ async Task IGuild.GetTextChannelAsync(ulong id, CacheMode mode, Re else return null; } + + /// + async Task IGuild.GetForumChannelAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetForumChannelAsync(id, options).ConfigureAwait(false); + else + return null; + } + + /// + async Task> IGuild.GetForumChannelsAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetForumChannelsAsync(options).ConfigureAwait(false); + else + return null; + } + + /// + async Task IGuild.GetMediaChannelAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMediaChannelAsync(id, options).ConfigureAwait(false); + else + return null; + } + + /// + async Task> IGuild.GetMediaChannelsAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetMediaChannelsAsync(options).ConfigureAwait(false); + else + return null; + } + /// async Task IGuild.GetThreadChannelAsync(ulong id, CacheMode mode, RequestOptions options) { @@ -1462,6 +1570,10 @@ async Task IGuild.CreateCategoryAsync(string name, Action IGuild.CreateForumChannelAsync(string name, Action func, RequestOptions options) => await CreateForumChannelAsync(name, func, options).ConfigureAwait(false); + /// + async Task IGuild.CreateMediaChannelAsync(string name, Action func, RequestOptions options) + => await CreateMediaChannelAsync(name, func, options).ConfigureAwait(false); + /// async Task> IGuild.GetVoiceRegionsAsync(RequestOptions options) => await GetVoiceRegionsAsync(options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs index 4860f60e58..52fb67262b 100644 --- a/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs +++ b/src/Discord.Net.Rest/Entities/Interactions/RestInteraction.cs @@ -201,7 +201,9 @@ ChannelType.Voice or ChannelType.Stage or ChannelType.NewsThread or ChannelType.PrivateThread or - ChannelType.PublicThread + ChannelType.PublicThread or + ChannelType.Media or + ChannelType.Forum => RestGuildChannel.Create(discord, Guild, model.Channel.Value) as IRestMessageChannel, ChannelType.DM => RestDMChannel.Create(discord, model.Channel.Value), ChannelType.Group => RestGroupChannel.Create(discord, model.Channel.Value), diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index bb4e461383..027b217db2 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -63,6 +63,7 @@ internal static SocketGuildChannel Create(SocketGuild guild, ClientState state, ChannelType.PrivateThread or ChannelType.PublicThread or ChannelType.NewsThread => SocketThreadChannel.Create(guild, state, model), ChannelType.Stage => SocketStageChannel.Create(guild, state, model), ChannelType.Forum => SocketForumChannel.Create(guild, state, model), + ChannelType.Media => SocketMediaChannel.Create(guild, state, model), _ => new SocketGuildChannel(guild.Discord, model.Id, guild), }; } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketMediaChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketMediaChannel.cs new file mode 100644 index 0000000000..ca03f1d638 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketMediaChannel.cs @@ -0,0 +1,24 @@ +using Model = Discord.API.Channel; + +namespace Discord.WebSocket; + +public class SocketMediaChannel : SocketForumChannel, IMediaChannel +{ + internal SocketMediaChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) + : base(discord, id, guild) + { + + } + + internal new static SocketMediaChannel Create(SocketGuild guild, ClientState state, Model model) + { + var entity = new SocketMediaChannel(guild?.Discord, model.Id, guild); + entity.Update(state, model); + return entity; + } + + internal override void Update(ClientState state, Model model) + { + base.Update(state, model); + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 53c5c93ee9..1740e37233 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -339,6 +339,15 @@ public IReadOnlyCollection ThreadChannels public IReadOnlyCollection ForumChannels => Channels.OfType().ToImmutableArray(); + /// + /// Gets a collection of all media channels in this guild. + /// + /// + /// A read-only collection of forum channels found within this guild. + /// + public IReadOnlyCollection MediaChannels + => Channels.OfType().ToImmutableArray(); + /// /// Gets the current logged-in user. /// @@ -790,6 +799,16 @@ public SocketStageChannel GetStageChannel(ulong id) public SocketCategoryChannel GetCategoryChannel(ulong id) => GetChannel(id) as SocketCategoryChannel; + /// + /// Gets a media channel in this guild. + /// + /// The snowflake identifier for the stage channel. + /// + /// A stage channel associated with the specified ; if none is found. + /// + public SocketMediaChannel GetMediaChannel(ulong id) + => GetChannel(id) as SocketMediaChannel; + /// /// Creates a new text channel in this guild. /// @@ -857,7 +876,7 @@ public Task CreateCategoryChannelAsync(string name, Action< => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options, func); /// - /// Creates a new channel forum in this guild. + /// Creates a new forum channel in this guild. /// /// The new name for the forum. /// The delegate containing the properties to be applied to the channel upon its creation. @@ -870,6 +889,20 @@ public Task CreateCategoryChannelAsync(string name, Action< public Task CreateForumChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateForumChannelAsync(this, Discord, name, options, func); + /// + /// Creates a new media channel in this guild. + /// + /// The new name for the media channel. + /// The delegate containing the properties to be applied to the channel upon its creation. + /// The options to be used when sending the request. + /// is . + /// + /// A task that represents the asynchronous creation operation. The task result contains the newly created + /// media channel. + /// + public Task CreateMediaChannelAsync(string name, Action func = null, RequestOptions options = null) + => GuildHelper.CreateMediaChannelAsync(this, Discord, name, options, func); + internal SocketGuildChannel AddChannel(ClientState state, ChannelModel model) { var channel = SocketGuildChannel.Create(this, state, model); @@ -2075,6 +2108,21 @@ Task IGuild.GetRulesChannelAsync(CacheMode mode, RequestOptions op /// Task IGuild.GetPublicUpdatesChannelAsync(CacheMode mode, RequestOptions options) => Task.FromResult(PublicUpdatesChannel); + + /// + Task IGuild.GetForumChannelAsync(ulong id, CacheMode mode, RequestOptions options) + => Task.FromResult(GetForumChannel(id)); + /// + Task> IGuild.GetForumChannelsAsync(CacheMode mode, RequestOptions options) + => Task.FromResult>(ForumChannels); + + /// + Task IGuild.GetMediaChannelAsync(ulong id, CacheMode mode, RequestOptions options) + => Task.FromResult(GetMediaChannel(id)); + /// + Task> IGuild.GetMediaChannelsAsync(CacheMode mode, RequestOptions options) + => Task.FromResult>(MediaChannels); + /// async Task IGuild.CreateTextChannelAsync(string name, Action func, RequestOptions options) => await CreateTextChannelAsync(name, func, options).ConfigureAwait(false); @@ -2091,6 +2139,10 @@ async Task IGuild.CreateCategoryAsync(string name, Action IGuild.CreateForumChannelAsync(string name, Action func, RequestOptions options) => await CreateForumChannelAsync(name, func, options).ConfigureAwait(false); + /// + async Task IGuild.CreateMediaChannelAsync(string name, Action func, RequestOptions options) + => await CreateMediaChannelAsync(name, func, options).ConfigureAwait(false); + /// async Task> IGuild.GetVoiceRegionsAsync(RequestOptions options) => await GetVoiceRegionsAsync(options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs b/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs index 3d54c62877..c659b7badc 100644 --- a/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs +++ b/src/Discord.Net.WebSocket/Entities/Invites/SocketInvite.cs @@ -39,6 +39,8 @@ ChannelType IInvite.ChannelType IGroupChannel groupChannel => ChannelType.Group, INewsChannel newsChannel => ChannelType.News, ITextChannel textChannel => ChannelType.Text, + IMediaChannel mediaChannel => ChannelType.Media, + IForumChannel forumChannel => ChannelType.Forum, _ => throw new InvalidOperationException("Invalid channel type."), }; }