diff --git a/src/Discord.Net.Core/Entities/Channels/GuildChannelCategoryProperties.cs b/src/Discord.Net.Core/Entities/Channels/GuildChannelCategoryProperties.cs new file mode 100644 index 000000000..2678e5eef --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/GuildChannelCategoryProperties.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + //public class GuildChannelCategoryProperties : GuildChannelProperties + //{ + //} +} diff --git a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs index 0ea196a4a..e580a2e59 100644 --- a/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs +++ b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs @@ -26,5 +26,9 @@ /// Move the channel to the following position. This is 0-based! /// public Optional Position { get; set; } + /// + /// Sets the category for this channel + /// + public Optional ParentId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 3d08a8c51..b019fab23 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -9,6 +9,10 @@ namespace Discord /// Gets the position of this channel in the guild's channel list, relative to others of the same type. int Position { get; } + /// Gets the parentid (category) of this channel in the guild's channel list. + ulong? ParentId { get; } + /// Gets the parent channel (category) of this channel. + Task GetParentChannelAsync(); /// Gets the guild this channel is a member of. IGuild Guild { get; } /// Gets the id of the guild this channel is a member of. @@ -23,7 +27,7 @@ namespace Discord Task CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); /// Returns a collection of all invites to this channel. Task> GetInvitesAsync(RequestOptions options = null); - + /// Modifies this guild channel. Task ModifyAsync(Action func, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannelCategory.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannelCategory.cs new file mode 100644 index 000000000..fa603e87b --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannelCategory.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IGuildChannelCategory : IGuildChannel + { + ///// Modifies this text channel. + //Task ModifyAsync(Action func, RequestOptions options = null); + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 3ded9e038..ee65fa3d4 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -84,6 +84,7 @@ namespace Discord Task> GetTextChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task GetTextChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task> GetVoiceChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + Task> GetChannelCategoriesAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task GetAFKChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task GetSystemChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/API/Common/Channel.cs b/src/Discord.Net.Rest/API/Common/Channel.cs index 608ddcf66..f986ab59b 100644 --- a/src/Discord.Net.Rest/API/Common/Channel.cs +++ b/src/Discord.Net.Rest/API/Common/Channel.cs @@ -23,6 +23,8 @@ namespace Discord.API public Optional Position { get; set; } [JsonProperty("permission_overwrites")] public Optional PermissionOverwrites { get; set; } + [JsonProperty("parent_id")] + public ulong? ParentId { get; set; } //TextChannel [JsonProperty("topic")] diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelCategoryParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelCategoryParams.cs new file mode 100644 index 000000000..293ade18e --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelCategoryParams.cs @@ -0,0 +1,14 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + internal class ModifyGuildChannelCategoryParams + { + [JsonProperty("name")] + public Optional Name { get; set; } + [JsonProperty("position")] + public Optional Position { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs index b4add2ac9..5bf615d60 100644 --- a/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs @@ -10,5 +10,7 @@ namespace Discord.API.Rest public Optional Name { get; set; } [JsonProperty("position")] public Optional Position { get; set; } + [JsonProperty("parent_id")] + public Optional ParentId { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index d61b5d14a..ebfd38e24 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -13,13 +13,13 @@ namespace Discord.Rest internal static class ChannelHelper { //General - public static async Task DeleteAsync(IChannel channel, BaseDiscordClient client, + public static async Task DeleteAsync(IChannel channel, BaseDiscordClient client, RequestOptions options) - { + { await client.ApiClient.DeleteChannelAsync(channel.Id, options).ConfigureAwait(false); } - public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client, - Action func, + public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client, + Action func, RequestOptions options) { var args = new GuildChannelProperties(); @@ -27,12 +27,13 @@ namespace Discord.Rest var apiArgs = new API.Rest.ModifyGuildChannelParams { Name = args.Name, - Position = args.Position + Position = args.Position, + ParentId = args.ParentId }; return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } - public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client, - Action func, + public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client, + Action func, RequestOptions options) { var args = new TextChannelProperties(); @@ -41,13 +42,14 @@ namespace Discord.Rest { Name = args.Name, Position = args.Position, + ParentId = args.ParentId, Topic = args.Topic, IsNsfw = args.IsNsfw }; return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } - public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, - Action func, + public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, + Action func, RequestOptions options) { var args = new VoiceChannelProperties(); @@ -57,6 +59,7 @@ namespace Discord.Rest Bitrate = args.Bitrate, Name = args.Name, Position = args.Position, + ParentId = args.ParentId, UserLimit = args.UserLimit.IsSpecified ? (args.UserLimit.Value ?? 0) : Optional.Create() }; return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); @@ -86,7 +89,7 @@ namespace Discord.Rest } //Messages - public static async Task GetMessageAsync(IMessageChannel channel, BaseDiscordClient client, + public static async Task GetMessageAsync(IMessageChannel channel, BaseDiscordClient client, ulong id, RequestOptions options) { var guildId = (channel as IGuildChannel)?.GuildId; @@ -97,7 +100,7 @@ namespace Discord.Rest var author = GetAuthor(client, guild, model.Author.Value, model.WebhookId.ToNullable()); return RestMessage.Create(client, channel, author, model); } - public static IAsyncEnumerable> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, + public static IAsyncEnumerable> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, ulong? fromMessageId, Direction dir, int limit, RequestOptions options) { if (dir == Direction.Around) @@ -123,7 +126,7 @@ namespace Discord.Rest foreach (var model in models) { var author = GetAuthor(client, guild, model.Author.Value, model.WebhookId.ToNullable()); - builder.Add(RestMessage.Create(client, channel, author, model)); + builder.Add(RestMessage.Create(client, channel, author, model)); } return builder.ToImmutable(); }, @@ -179,7 +182,7 @@ namespace Discord.Rest var args = new UploadFileParams(stream) { Filename = filename, Content = text, IsTTS = isTTS }; var model = await client.ApiClient.UploadFileAsync(channel.Id, args, options).ConfigureAwait(false); return RestUserMessage.Create(client, channel, client.CurrentUser, model); - } + } public static async Task DeleteMessagesAsync(IMessageChannel channel, BaseDiscordClient client, IEnumerable messageIds, RequestOptions options) @@ -276,7 +279,7 @@ namespace Discord.Rest { await client.ApiClient.TriggerTypingIndicatorAsync(channel.Id, options).ConfigureAwait(false); } - public static IDisposable EnterTypingState(IMessageChannel channel, BaseDiscordClient client, + public static IDisposable EnterTypingState(IMessageChannel channel, BaseDiscordClient client, RequestOptions options) => new TypingNotifier(client, channel, options); diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs index f05f1598e..e9f069a50 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs @@ -5,6 +5,7 @@ Text = 0, DM = 1, Voice = 2, - Group = 3 + Group = 3, + Category = 4 } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 07832a3a9..4524adf27 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -16,6 +16,8 @@ namespace Discord.Rest internal IGuild Guild { get; } public string Name { get; private set; } public int Position { get; private set; } + public ulong? ParentId { get; private set; } + public Task GetParentChannelAsync() => ParentId == null ? null : Guild.GetChannelAsync(ParentId.Value); public ulong GuildId => Guild.Id; @@ -61,7 +63,7 @@ namespace Discord.Rest } public Task DeleteAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); - + public OverwritePermissions? GetPermissionOverwrite(IUser user) { for (int i = 0; i < _overwrites.Length; i++) @@ -139,20 +141,20 @@ namespace Discord.Rest => await GetInvitesAsync(options).ConfigureAwait(false); async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) => await CreateInviteAsync(maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); - - OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) + + OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IRole role) => GetPermissionOverwrite(role); OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) => GetPermissionOverwrite(user); - async Task IGuildChannel.AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options) + async Task IGuildChannel.AddPermissionOverwriteAsync(IRole role, OverwritePermissions permissions, RequestOptions options) => await AddPermissionOverwriteAsync(role, permissions, options).ConfigureAwait(false); - async Task IGuildChannel.AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options) + async Task IGuildChannel.AddPermissionOverwriteAsync(IUser user, OverwritePermissions permissions, RequestOptions options) => await AddPermissionOverwriteAsync(user, permissions, options).ConfigureAwait(false); - async Task IGuildChannel.RemovePermissionOverwriteAsync(IRole role, RequestOptions options) + async Task IGuildChannel.RemovePermissionOverwriteAsync(IRole role, RequestOptions options) => await RemovePermissionOverwriteAsync(role, options).ConfigureAwait(false); - async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user, RequestOptions options) + async Task IGuildChannel.RemovePermissionOverwriteAsync(IUser user, RequestOptions options) => await RemovePermissionOverwriteAsync(user, options).ConfigureAwait(false); - + IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => AsyncEnumerable.Empty>(); //Overriden //Overriden in Text/Voice Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannelCategory.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannelCategory.cs new file mode 100644 index 000000000..e4a59b30a --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannelCategory.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Channel; + +namespace Discord.Rest +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RestGuildChannelCategory : RestGuildChannel, IGuildChannelCategory + { + public string Mention => MentionUtils.MentionChannel(Id); + + internal RestGuildChannelCategory(BaseDiscordClient discord, IGuild guild, ulong id) + : base(discord, guild, id) + { + } + internal new static RestGuildChannelCategory Create(BaseDiscordClient discord, IGuild guild, Model model) + { + var entity = new RestGuildChannelCategory(discord, guild, model.Id); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + } + + public Task GetUserAsync(ulong id, RequestOptions options = null) + => ChannelHelper.GetUserAsync(this, Guild, Discord, id, options); + public IAsyncEnumerable> GetUsersAsync(RequestOptions options = null) + => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); + + private string DebuggerDisplay => $"{Name} ({Id}, Text)"; + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index aee305951..66e6ba739 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -23,7 +23,7 @@ namespace Discord.Rest public VerificationLevel VerificationLevel { get; private set; } public MfaLevel MfaLevel { get; private set; } public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } - + public ulong? AFKChannelId { get; private set; } public ulong? EmbedChannelId { get; private set; } public ulong? SystemChannelId { get; private set; } @@ -114,7 +114,7 @@ namespace Discord.Rest Update(model); } public async Task ModifyEmbedAsync(Action func, RequestOptions options = null) - { + { var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); } @@ -155,7 +155,7 @@ namespace Discord.Rest public Task> GetChannelsAsync(RequestOptions options = null) => GuildHelper.GetChannelsAsync(this, Discord, options); public Task GetChannelAsync(ulong id, RequestOptions options = null) - => GuildHelper.GetChannelAsync(this, Discord, id, options); + => GuildHelper.GetChannelAsync(this, Discord, id, options); public async Task GetTextChannelAsync(ulong id, RequestOptions options = null) { var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false); @@ -176,6 +176,11 @@ namespace Discord.Rest var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); return channels.Select(x => x as RestVoiceChannel).Where(x => x != null).ToImmutableArray(); } + public async Task> GetChannelCategoriesAsync(RequestOptions options = null) + { + var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); + return channels.Select(x => x as RestGuildChannelCategory).Where(x => x != null).ToImmutableArray(); + } public async Task GetAFKChannelAsync(RequestOptions options = null) { @@ -199,7 +204,7 @@ namespace Discord.Rest public async Task GetEmbedChannelAsync(RequestOptions options = null) { var embedId = EmbedChannelId; - if (embedId.HasValue) + if (embedId.HasValue) return await GuildHelper.GetChannelAsync(this, Discord, embedId.Value, options).ConfigureAwait(false); return null; } @@ -236,7 +241,7 @@ namespace Discord.Rest return null; } - public async Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), + public async Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false, RequestOptions options = null) { var role = await GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted, options).ConfigureAwait(false); @@ -304,6 +309,13 @@ namespace Discord.Rest else return ImmutableArray.Create(); } + async Task> IGuild.GetChannelCategoriesAsync(CacheMode mode , RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetChannelCategoriesAsync(options).ConfigureAwait(false); + else + return null; + } async Task IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -352,7 +364,7 @@ namespace Discord.Rest async Task> IGuild.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); - IRole IGuild.GetRole(ulong id) + IRole IGuild.GetRole(ulong id) => GetRole(id); async Task IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) => await CreateRoleAsync(name, permissions, color, isHoisted, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcChannelCategory.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcChannelCategory.cs new file mode 100644 index 000000000..229753cf4 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcChannelCategory.cs @@ -0,0 +1,38 @@ +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Rpc.Channel; + +namespace Discord.Rpc +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class RpcChannelCategory : RpcGuildChannel + { + public IReadOnlyCollection CachedMessages { get; private set; } + + public string Mention => MentionUtils.MentionChannel(Id); + // TODO: Check if RPC includes the 'nsfw' field on Channel models + public bool IsNsfw => ChannelHelper.IsNsfw(this); + + internal RpcChannelCategory(DiscordRpcClient discord, ulong id, ulong guildId) + : base(discord, id, guildId) + { + } + internal new static RpcChannelCategory Create(DiscordRpcClient discord, Model model) + { + var entity = new RpcChannelCategory(discord, model.Id, model.GuildId.Value); + entity.Update(model); + return entity; + } + internal override void Update(Model model) + { + base.Update(model); + CachedMessages = model.Messages.Select(x => RpcMessage.Create(Discord, Id, x)).ToImmutableArray(); + } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 48eb8ec3e..02f04df93 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -10,6 +10,7 @@ namespace Discord.Rpc { public ulong GuildId { get; } public int Position { get; private set; } + public ulong? ParentId { get; private set; } internal RpcGuildChannel(DiscordRpcClient discord, ulong id, ulong guildId) : base(discord, id) @@ -57,6 +58,12 @@ namespace Discord.Rpc public override string ToString() => Name; //IGuildChannel + public Task GetParentChannelAsync() + { + //Always fails + throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); + } + IGuild IGuildChannel.Guild { get diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 1fe9a741f..64233f742 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -17,6 +17,9 @@ namespace Discord.WebSocket public SocketGuild Guild { get; } public string Name { get; private set; } public int Position { get; private set; } + public ulong? ParentId { get; private set; } + public IGuildChannel ParentChannel => ParentId == null ? null : Guild.GetChannel(ParentId.Value); + public Task GetParentChannelAsync() => Task.FromResult(ParentChannel); public IReadOnlyCollection PermissionOverwrites => _overwrites; public new virtual IReadOnlyCollection Users => ImmutableArray.Create(); @@ -34,6 +37,8 @@ namespace Discord.WebSocket return SocketTextChannel.Create(guild, state, model); case ChannelType.Voice: return SocketVoiceChannel.Create(guild, state, model); + case ChannelType.Category: + return SocketGuildChannelCategory.Create(guild, state, model); default: // TODO: Proper implementation for channel categories return new SocketGuildChannel(guild.Discord, model.Id, guild); @@ -43,6 +48,7 @@ namespace Discord.WebSocket { Name = model.Name.Value; Position = model.Position.Value; + ParentId = model.ParentId; var overwrites = model.PermissionOverwrites.Value; var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannelCategory.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannelCategory.cs new file mode 100644 index 000000000..d23060f78 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannelCategory.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Discord.Audio; +using Discord.Rest; +using Model = Discord.API.Channel; + +namespace Discord.WebSocket +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class SocketGuildChannelCategory : SocketGuildChannel, IGuildChannelCategory + { + public override IReadOnlyCollection Users + => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); + + internal SocketGuildChannelCategory(DiscordSocketClient discord, ulong id, SocketGuild guild) + : base(discord, id, guild) + { + } + internal new static SocketGuildChannelCategory Create(SocketGuild guild, ClientState state, Model model) + { + var entity = new SocketGuildChannelCategory(guild.Discord, model.Id, guild); + entity.Update(state, model); + return entity; + } + internal override void Update(ClientState state, Model model) + { + base.Update(state, model); + } + + public override SocketGuildUser GetUser(ulong id) + { + var user = Guild.GetUser(id); + if (user?.VoiceChannel?.Id == Id) + return user; + return null; + } + + private string DebuggerDisplay => $"{Name} ({Id}, Category)"; + internal new SocketGuildChannelCategory Clone() => MemberwiseClone() as SocketGuildChannelCategory; + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index b47ca84e8..b2529951a 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -74,7 +74,7 @@ namespace Discord.WebSocket return id.HasValue ? GetVoiceChannel(id.Value) : null; } } - public SocketGuildChannel EmbedChannel + public SocketGuildChannel EmbedChannel { get { @@ -94,6 +94,8 @@ namespace Discord.WebSocket => Channels.Select(x => x as SocketTextChannel).Where(x => x != null).ToImmutableArray(); public IReadOnlyCollection VoiceChannels => Channels.Select(x => x as SocketVoiceChannel).Where(x => x != null).ToImmutableArray(); + public IReadOnlyCollection ChannelCategories + => Channels.Select(x => x as SocketGuildChannelCategory).Where(x => x != null).ToImmutableArray(); public SocketGuildUser CurrentUser => _members.TryGetValue(Discord.CurrentUser.Id, out SocketGuildUser member) ? member : null; public SocketRole EveryoneRole => GetRole(Id); public IReadOnlyCollection Channels @@ -347,7 +349,7 @@ namespace Discord.WebSocket return value; return null; } - public Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), + public Task CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), bool isHoisted = false, RequestOptions options = null) => GuildHelper.CreateRoleAsync(this, Discord, name, permissions, color, isHoisted, options); internal SocketRole AddRole(RoleModel model) @@ -577,7 +579,7 @@ namespace Discord.WebSocket try { await RepopulateAudioStreamsAsync().ConfigureAwait(false); - await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); + await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); } catch (OperationCanceledException) { @@ -634,6 +636,8 @@ namespace Discord.WebSocket => Task.FromResult(GetTextChannel(id)); Task> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(VoiceChannels); + Task> IGuild.GetChannelCategoriesAsync(CacheMode mode , RequestOptions options) + => Task.FromResult>(ChannelCategories); Task IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetVoiceChannel(id)); Task IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options)