[Feature] Media channel support (#2725)

* initial commit

* oops

* another typo -_-

* Update AttachmentFlags.cs

Made this on my phone lol

* Update AttachmentFlags.cs

* -line

* initial impl

* some guild methods for media (and forum) channels

* file attachment can be a thumbnail

* can't edit media channel layout

* updatess

* Update ChannelPermissions.cs

* typo
This commit is contained in:
Mihail Gribkov
2023-11-18 23:56:14 +03:00
committed by GitHub
parent 9cedfbcdd9
commit e3cd340dcc
22 changed files with 468 additions and 52 deletions

View File

@@ -16,7 +16,12 @@ public enum ChannelFlags
Pinned = 1 << 1,
/// <summary>
/// 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.
/// </summary>
RequireTag = 1 << 4
RequireTag = 1 << 4,
/// <summary>
/// Flag given to a media channel that hides the embedded media download options.
/// </summary>
HideMediaDownloadOption = 1 << 15,
}

View File

@@ -1,33 +1,75 @@
namespace Discord
namespace Discord;
/// <summary> Defines the types of channels. </summary>
public enum ChannelType
{
/// <summary> Defines the types of channels. </summary>
public enum ChannelType
{
/// <summary> The channel is a text channel. </summary>
/// <summary>
/// The channel is a text channel.
/// </summary>
Text = 0,
/// <summary> The channel is a Direct Message channel. </summary>
/// <summary>
/// The channel is a Direct Message channel.
/// </summary>
DM = 1,
/// <summary> The channel is a voice channel. </summary>
/// <summary>
/// The channel is a voice channel.
/// </summary>
Voice = 2,
/// <summary> The channel is a group channel. </summary>
/// <summary>
/// The channel is a group channel.
/// </summary>
Group = 3,
/// <summary> The channel is a category channel. </summary>
/// <summary>
/// The channel is a category channel.
/// </summary>
Category = 4,
/// <summary> The channel is a news channel. </summary>
/// <summary>
/// The channel is a news channel.
/// </summary>
News = 5,
/// <summary> The channel is a store channel. </summary>
/// <summary>
/// The channel is a store channel.
/// </summary>
Store = 6,
/// <summary> The channel is a temporary thread channel under a news channel. </summary>
/// <summary>
/// The channel is a temporary thread channel under a news channel.
/// </summary>
NewsThread = 10,
/// <summary> The channel is a temporary thread channel under a text channel. </summary>
/// <summary>
/// The channel is a temporary thread channel under a text channel.
/// </summary>
PublicThread = 11,
/// <summary> The channel is a private temporary thread channel under a text channel. </summary>
/// <summary>
/// The channel is a private temporary thread channel under a text channel.
/// </summary>
PrivateThread = 12,
/// <summary> The channel is a stage voice channel. </summary>
/// <summary>
/// The channel is a stage voice channel.
/// </summary>
Stage = 13,
/// <summary> The channel is a guild directory used in hub servers. (Unreleased)</summary>
/// <summary>
/// The channel is a guild directory used in hub servers. (Unreleased)
/// </summary>
GuildDirectory = 14,
/// <summary> The channel is a forum channel containing multiple threads. </summary>
Forum = 15
}
/// <summary>
/// The channel is a forum channel containing multiple threads.
/// </summary>
Forum = 15,
/// <summary>
/// The channel is a media channel containing multiple threads.
/// </summary>
Media = 16,
}

View File

@@ -46,5 +46,8 @@ public class ForumChannelProperties : TextChannelProperties
/// <summary>
/// Gets or sets the rule used to display posts in a forum channel.
/// </summary>
/// <remarks>
/// This property cannot be changed in media channels.
/// </remarks>
public Optional<ForumLayout> DefaultLayout { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace Discord;
/// <summary>
/// Represents a media channel in a guild that can create posts.
/// </summary>
public interface IMediaChannel : IForumChannel
{
}

View File

@@ -733,6 +733,52 @@ namespace Discord
/// </returns>
Task<IReadOnlyCollection<IThreadChannel>> GetThreadChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary>
/// Gets a forum channel in this guild.
/// </summary>
/// <param name="id">The snowflake identifier for the stage channel.</param>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains the stage channel associated
/// with the specified <paramref name="id"/>; <see langword="null" /> if none is found.
/// </returns>
Task<IForumChannel> GetForumChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary>
/// Gets a collection of all forum channels in this guild.
/// </summary>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// forum channels found within this guild.
/// </returns>
Task<IReadOnlyCollection<IForumChannel>> GetForumChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary>
/// Gets a forum channel in this guild.
/// </summary>
/// <param name="id">The snowflake identifier for the stage channel.</param>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains the stage channel associated
/// with the specified <paramref name="id"/>; <see langword="null" /> if none is found.
/// </returns>
Task<IMediaChannel> GetMediaChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary>
/// Gets a collection of all forum channels in this guild.
/// </summary>
/// <param name="mode">The <see cref="CacheMode"/> that determines whether the object should be fetched from cache.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// media channels found within this guild.
/// </returns>
Task<IReadOnlyCollection<IMediaChannel>> GetMediaChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
/// <summary>
/// Creates a new text channel in this guild.
/// </summary>
@@ -795,6 +841,18 @@ namespace Discord
/// </returns>
Task<IForumChannel> CreateForumChannelAsync(string name, Action<ForumChannelProperties> func = null, RequestOptions options = null);
/// <summary>
/// Creates a new media channel in this guild.
/// </summary>
/// <param name="name">The new name for the media channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous creation operation. The task result contains the newly created
/// forum channel.
/// </returns>
Task<IMediaChannel> CreateMediaChannelAsync(string name, Action<ForumChannelProperties> func = null, RequestOptions options = null);
/// <summary>
/// Gets a collection of all the voice regions this guild can access.
/// </summary>

View File

@@ -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 @@ namespace Discord
/// </summary>
public bool IsSpoiler { get; set; }
/// <summary>
/// Gets or sets if this file should be a thumbnail for a media channel post.
/// </summary>
public bool IsThumbnail { get; set; }
#pragma warning disable IDISP008
/// <summary>
/// Gets the stream containing the file content.
@@ -42,12 +43,14 @@ namespace Discord
/// <param name="fileName">The name of the attachment.</param>
/// <param name="description">The description of the attachment.</param>
/// <param name="isSpoiler">Whether or not the attachment is a spoiler.</param>
public FileAttachment(Stream stream, string fileName, string description = null, bool isSpoiler = false)
/// <param name="isThumbnail">Whether or not this attachment should be a thumbnail for a media channel post.</param>
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 @@ namespace Discord
/// <param name="fileName">The name of the attachment.</param>
/// <param name="description">The description of the attachment.</param>
/// <param name="isSpoiler">Whether or not the attachment is a spoiler.</param>
/// <param name="isThumbnail">Whether or not this attachment should be a thumbnail for a media channel post.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="path" /> is a zero-length string, contains only white space, or contains one or more invalid
/// characters as defined by <see cref="Path.GetInvalidPathChars"/>.
@@ -87,13 +91,14 @@ namespace Discord
/// <exception cref="FileNotFoundException">The file specified in <paramref name="path" /> was not found.
/// </exception>
/// <exception cref="IOException">An I/O error occurred while opening the file. </exception>
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()

View File

@@ -50,6 +50,11 @@ namespace Discord
/// </summary>
public static readonly ChannelPermissions Forum = new(0b01_001110_010010_110011_111101_111111_111101_010001);
/// <summary>
/// Gets a <see cref="ChannelPermissions"/> that grants all permissions for media channels.
/// </summary>
public static readonly ChannelPermissions Media = new(0b01_001110_010010_110011_111101_111111_111101_010001);
/// <summary>
/// Gets a <see cref="ChannelPermissions"/> that grants all permissions for a given channel type.
/// </summary>
@@ -64,6 +69,7 @@ namespace Discord
ICategoryChannel _ => Category,
IDMChannel _ => DM,
IGroupChannel _ => Group,
IMediaChannel _ => Media,
IForumChannel => Forum,
_ => throw new ArgumentException(message: "Unknown channel type.", paramName: nameof(channel)),
};

View File

@@ -47,6 +47,9 @@ namespace Discord
case ITextChannel:
return ChannelType.Text;
case IMediaChannel:
return ChannelType.Media;
case IForumChannel:
return ChannelType.Forum;
}

View File

@@ -9,6 +9,6 @@ public static class ChannelTypeUtils
{
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
};
}

View File

@@ -69,6 +69,12 @@ namespace Discord.Interactions
_ when typeof(ITextChannel).IsAssignableFrom(type)
=> new List<ChannelType> { ChannelType.Text },
_ when typeof(IMediaChannel).IsAssignableFrom(type)
=> new List<ChannelType> { ChannelType.Media },
_ when typeof(IForumChannel).IsAssignableFrom(type)
=> new List<ChannelType> { ChannelType.Forum },
_ => null
};
}

View File

@@ -78,7 +78,8 @@ namespace Discord.API.Rest
{
id = (ulong)n,
filename = filename,
description = attachment.Description ?? Optional<string>.Unspecified
description = attachment.Description ?? Optional<string>.Unspecified,
is_thumbnail = attachment.IsThumbnail,
});
}

View File

@@ -57,8 +57,7 @@ internal static class ForumHelper
emoji.Name : Optional<string>.Unspecified
}
: Optional<ModifyForumReactionEmojiParams>.Unspecified,
DefaultSortOrder = args.DefaultSortOrder,
DefaultLayout = args.DefaultLayout,
DefaultSortOrder = args.DefaultSortOrder
};
return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false);
}

View File

@@ -50,7 +50,8 @@ namespace Discord.Rest
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),

View File

@@ -42,6 +42,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.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),

View File

@@ -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);
}
}

View File

@@ -428,6 +428,66 @@ namespace Discord.Rest
var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false);
return RestForumChannel.Create(client, guild, model);
}
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null" />.</exception>
public static async Task<RestMediaChannel> CreateMediaChannelAsync(IGuild guild, BaseDiscordClient client,
string name, RequestOptions options, Action<ForumChannelProperties> 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<API.Overwrite[]>(),
SlowModeInterval = props.ThreadCreationInterval,
AvailableTags = props.Tags.GetValueOrDefault(Array.Empty<ForumTagProperties>()).Select(
x => new ModifyForumTagParams
{
Id = x.Id,
Name = x.Name,
EmojiId = x.Emoji is Emote emote
? emote.Id
: Optional<ulong?>.Unspecified,
EmojiName = x.Emoji is Emoji emoji
? emoji.Name
: Optional<string>.Unspecified,
Moderated = x.IsModerated
}).ToArray(),
DefaultReactionEmoji = props.DefaultReactionEmoji.IsSpecified
? new API.ModifyForumReactionEmojiParams
{
EmojiId = props.DefaultReactionEmoji.Value is Emote emote ?
emote.Id : Optional<ulong?>.Unspecified,
EmojiName = props.DefaultReactionEmoji.Value is Emoji emoji ?
emoji.Name : Optional<string>.Unspecified
}
: Optional<ModifyForumReactionEmojiParams>.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

View File

@@ -462,6 +462,64 @@ namespace Discord.Rest
return channels.OfType<RestTextChannel>().ToImmutableArray();
}
/// <summary>
/// Gets a forum channel in this guild.
/// </summary>
/// <param name="id">The snowflake identifier for the forum channel.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains the text channel
/// associated with the specified <paramref name="id"/>; <see langword="null"/> if none is found.
/// </returns>
public async Task<RestForumChannel> GetForumChannelAsync(ulong id, RequestOptions options = null)
{
var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false);
return channel as RestForumChannel;
}
/// <summary>
/// Gets a collection of all forum channels in this guild.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// forum channels found within this guild.
/// </returns>
public async Task<IReadOnlyCollection<RestForumChannel>> GetForumChannelsAsync(RequestOptions options = null)
{
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false);
return channels.OfType<RestForumChannel>().ToImmutableArray();
}
/// <summary>
/// Gets a media channel in this guild.
/// </summary>
/// <param name="id">The snowflake identifier for the text channel.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains the media channel
/// associated with the specified <paramref name="id"/>; <see langword="null"/> if none is found.
/// </returns>
public async Task<RestMediaChannel> GetMediaChannelAsync(ulong id, RequestOptions options = null)
{
var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false);
return channel as RestMediaChannel;
}
/// <summary>
/// Gets a collection of all media channels in this guild.
/// </summary>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous get operation. The task result contains a read-only collection of
/// media channels found within this guild.
/// </returns>
public async Task<IReadOnlyCollection<RestMediaChannel>> GetMediaChannelsAsync(RequestOptions options = null)
{
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false);
return channels.OfType<RestMediaChannel>().ToImmutableArray();
}
/// <summary>
/// Gets a thread channel in this guild.
/// </summary>
@@ -735,18 +793,31 @@ namespace Discord.Rest
=> GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options, func);
/// <summary>
/// Creates a category channel with the provided name.
/// Creates a new forum channel with the provided name.
/// </summary>
/// <param name="name">The name of the new channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <exception cref="ArgumentNullException"><paramref name="name" /> is <see langword="null"/>.</exception>
/// <returns>
/// The created category channel.
/// The created forum channel.
/// </returns>
public Task<RestForumChannel> CreateForumChannelAsync(string name, Action<ForumChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateForumChannelAsync(this, Discord, name, options, func);
/// <summary>
/// Creates a new media channel in this guild.
/// </summary>
/// <param name="name">The new name for the media channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <returns>
/// A task that represents the asynchronous creation operation. The task result contains the newly created
/// media channel.
/// </returns>
public Task<RestMediaChannel> CreateMediaChannelAsync(string name, Action<ForumChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateMediaChannelAsync(this, Discord, name, options, func);
/// <summary>
/// Gets a collection of all the voice regions this guild can access.
/// </summary>
@@ -1342,6 +1413,43 @@ namespace Discord.Rest
else
return null;
}
/// <inheritdoc />
async Task<IForumChannel> IGuild.GetForumChannelAsync(ulong id, CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
return await GetForumChannelAsync(id, options).ConfigureAwait(false);
else
return null;
}
/// <inheritdoc />
async Task<IReadOnlyCollection<IForumChannel>> IGuild.GetForumChannelsAsync(CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
return await GetForumChannelsAsync(options).ConfigureAwait(false);
else
return null;
}
/// <inheritdoc />
async Task<IMediaChannel> IGuild.GetMediaChannelAsync(ulong id, CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
return await GetMediaChannelAsync(id, options).ConfigureAwait(false);
else
return null;
}
/// <inheritdoc />
async Task<IReadOnlyCollection<IMediaChannel>> IGuild.GetMediaChannelsAsync(CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
return await GetMediaChannelsAsync(options).ConfigureAwait(false);
else
return null;
}
/// <inheritdoc />
async Task<IThreadChannel> IGuild.GetThreadChannelAsync(ulong id, CacheMode mode, RequestOptions options)
{
@@ -1462,6 +1570,10 @@ namespace Discord.Rest
async Task<IForumChannel> IGuild.CreateForumChannelAsync(string name, Action<ForumChannelProperties> func, RequestOptions options)
=> await CreateForumChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IMediaChannel> IGuild.CreateMediaChannelAsync(string name, Action<ForumChannelProperties> func, RequestOptions options)
=> await CreateMediaChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IVoiceRegion>> IGuild.GetVoiceRegionsAsync(RequestOptions options)
=> await GetVoiceRegionsAsync(options).ConfigureAwait(false);

View File

@@ -201,7 +201,9 @@ namespace Discord.Rest
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),

View File

@@ -63,6 +63,7 @@ namespace Discord.WebSocket
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),
};
}

View File

@@ -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);
}
}

View File

@@ -339,6 +339,15 @@ namespace Discord.WebSocket
public IReadOnlyCollection<SocketForumChannel> ForumChannels
=> Channels.OfType<SocketForumChannel>().ToImmutableArray();
/// <summary>
/// Gets a collection of all media channels in this guild.
/// </summary>
/// <returns>
/// A read-only collection of forum channels found within this guild.
/// </returns>
public IReadOnlyCollection<SocketMediaChannel> MediaChannels
=> Channels.OfType<SocketMediaChannel>().ToImmutableArray();
/// <summary>
/// Gets the current logged-in user.
/// </summary>
@@ -790,6 +799,16 @@ namespace Discord.WebSocket
public SocketCategoryChannel GetCategoryChannel(ulong id)
=> GetChannel(id) as SocketCategoryChannel;
/// <summary>
/// Gets a media channel in this guild.
/// </summary>
/// <param name="id">The snowflake identifier for the stage channel.</param>
/// <returns>
/// A stage channel associated with the specified <paramref name="id" />; <see langword="null"/> if none is found.
/// </returns>
public SocketMediaChannel GetMediaChannel(ulong id)
=> GetChannel(id) as SocketMediaChannel;
/// <summary>
/// Creates a new text channel in this guild.
/// </summary>
@@ -857,7 +876,7 @@ namespace Discord.WebSocket
=> GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options, func);
/// <summary>
/// Creates a new channel forum in this guild.
/// Creates a new forum channel in this guild.
/// </summary>
/// <param name="name">The new name for the forum.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
@@ -870,6 +889,20 @@ namespace Discord.WebSocket
public Task<RestForumChannel> CreateForumChannelAsync(string name, Action<ForumChannelProperties> func = null, RequestOptions options = null)
=> GuildHelper.CreateForumChannelAsync(this, Discord, name, options, func);
/// <summary>
/// Creates a new media channel in this guild.
/// </summary>
/// <param name="name">The new name for the media channel.</param>
/// <param name="func">The delegate containing the properties to be applied to the channel upon its creation.</param>
/// <param name="options">The options to be used when sending the request.</param>
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null"/>.</exception>
/// <returns>
/// A task that represents the asynchronous creation operation. The task result contains the newly created
/// media channel.
/// </returns>
public Task<RestMediaChannel> CreateMediaChannelAsync(string name, Action<ForumChannelProperties> 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 @@ namespace Discord.WebSocket
/// <inheritdoc />
Task<ITextChannel> IGuild.GetPublicUpdatesChannelAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<ITextChannel>(PublicUpdatesChannel);
/// <inheritdoc />
Task<IForumChannel> IGuild.GetForumChannelAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult<IForumChannel>(GetForumChannel(id));
/// <inheritdoc />
Task<IReadOnlyCollection<IForumChannel>> IGuild.GetForumChannelsAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<IReadOnlyCollection<IForumChannel>>(ForumChannels);
/// <inheritdoc />
Task<IMediaChannel> IGuild.GetMediaChannelAsync(ulong id, CacheMode mode, RequestOptions options)
=> Task.FromResult<IMediaChannel>(GetMediaChannel(id));
/// <inheritdoc />
Task<IReadOnlyCollection<IMediaChannel>> IGuild.GetMediaChannelsAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult<IReadOnlyCollection<IMediaChannel>>(MediaChannels);
/// <inheritdoc />
async Task<ITextChannel> IGuild.CreateTextChannelAsync(string name, Action<TextChannelProperties> func, RequestOptions options)
=> await CreateTextChannelAsync(name, func, options).ConfigureAwait(false);
@@ -2091,6 +2139,10 @@ namespace Discord.WebSocket
async Task<IForumChannel> IGuild.CreateForumChannelAsync(string name, Action<ForumChannelProperties> func, RequestOptions options)
=> await CreateForumChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IMediaChannel> IGuild.CreateMediaChannelAsync(string name, Action<ForumChannelProperties> func, RequestOptions options)
=> await CreateMediaChannelAsync(name, func, options).ConfigureAwait(false);
/// <inheritdoc />
async Task<IReadOnlyCollection<IVoiceRegion>> IGuild.GetVoiceRegionsAsync(RequestOptions options)
=> await GetVoiceRegionsAsync(options).ConfigureAwait(false);

View File

@@ -39,6 +39,8 @@ namespace Discord.WebSocket
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."),
};
}