committags/2.0.0-betaa85c5814a7Author: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 6 22:25:48 2018 -0500 Code cleanup commit4b243fd3ddAuthor: Christopher F <computerizedtaco@gmail.com> Date: Sat Jan 6 22:08:28 2018 -0500 Add support for channel categories (#907) commit41ed9106f2Author: mrspits4ever <spits.lucas@gmail.com> Date: Thu Dec 14 20:02:57 2017 +0100 removed mentioning support for RestCategoryChannel, added channels property to SocketCategoryChannel commit71142c3108Merge:4589d731678a7238Author: mrspits4ever <spits.lucas@gmail.com> Date: Wed Dec 13 21:17:53 2017 +0100 Merge branch 'dev' of https://github.com/RogueException/Discord.Net into feature/channel-categories commit4589d73187Author: mrspits4ever <spits.lucas@gmail.com> Date: Wed Dec 13 21:17:46 2017 +0100 adressed requested changes commitd59b038efaAuthor: pegasy <pegasy@users.noreply.github.com> Date: Mon Sep 25 18:53:23 2017 +0200 Renamed classes / properties / methods to use CategoryChannel instead of ChannelCategory to be consistant with how text / voice channels are named. commit5c4777dc8cAuthor: pegasy <pegasy@users.noreply.github.com> Date: Sun Sep 24 19:08:25 2017 +0200 removed Guild from class name for ChannelCategory Renamed all properties to use Category instead of Parent Throw exception on GetUsers / GetInvites etc for categories commite18bd8c799Author: pegasy <pegasy@users.noreply.github.com> Date: Sun Sep 24 15:49:51 2017 +0200 Add support for channel categories (as its own channel type)
| @@ -10,6 +10,7 @@ namespace Discord.Rpc | |||
| { | |||
| public ulong GuildId { get; } | |||
| public int Position { get; private set; } | |||
| public ulong? CategoryId { 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<ICategoryChannel> GetCategoryAsync() | |||
| { | |||
| //Always fails | |||
| throw new InvalidOperationException("Unable to return this entity's parent unless it was fetched through that object."); | |||
| } | |||
| IGuild IGuildChannel.Guild | |||
| { | |||
| get | |||
| @@ -26,5 +26,9 @@ | |||
| /// Move the channel to the following position. This is 0-based! | |||
| /// </summary> | |||
| public Optional<int> Position { get; set; } | |||
| /// <summary> | |||
| /// Sets the category for this channel | |||
| /// </summary> | |||
| public Optional<ulong?> CategoryId { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,12 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace Discord | |||
| { | |||
| public interface ICategoryChannel : IGuildChannel | |||
| { | |||
| } | |||
| } | |||
| @@ -9,6 +9,10 @@ namespace Discord | |||
| /// <summary> Gets the position of this channel in the guild's channel list, relative to others of the same type. </summary> | |||
| int Position { get; } | |||
| /// <summary> Gets the parentid (category) of this channel in the guild's channel list. </summary> | |||
| ulong? CategoryId { get; } | |||
| /// <summary> Gets the parent channel (category) of this channel. </summary> | |||
| Task<ICategoryChannel> GetCategoryAsync(); | |||
| /// <summary> Gets the guild this channel is a member of. </summary> | |||
| IGuild Guild { get; } | |||
| /// <summary> Gets the id of the guild this channel is a member of. </summary> | |||
| @@ -23,7 +27,7 @@ namespace Discord | |||
| Task<IInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int? maxUses = default(int?), bool isTemporary = false, bool isUnique = false, RequestOptions options = null); | |||
| /// <summary> Returns a collection of all invites to this channel. </summary> | |||
| Task<IReadOnlyCollection<IInviteMetadata>> GetInvitesAsync(RequestOptions options = null); | |||
| /// <summary> Modifies this guild channel. </summary> | |||
| Task ModifyAsync(Action<GuildChannelProperties> func, RequestOptions options = null); | |||
| @@ -84,6 +84,7 @@ namespace Discord | |||
| Task<IReadOnlyCollection<ITextChannel>> GetTextChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<ITextChannel> GetTextChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<IReadOnlyCollection<IVoiceChannel>> GetVoiceChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<IReadOnlyCollection<ICategoryChannel>> GetCategoriesAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<IVoiceChannel> GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<IVoiceChannel> GetAFKChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| Task<ITextChannel> GetSystemChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); | |||
| @@ -93,6 +94,8 @@ namespace Discord | |||
| Task<ITextChannel> CreateTextChannelAsync(string name, RequestOptions options = null); | |||
| /// <summary> Creates a new voice channel. </summary> | |||
| Task<IVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null); | |||
| /// <summary> Creates a new channel category. </summary> | |||
| Task<ICategoryChannel> CreateCategoryAsync(string name, RequestOptions options = null); | |||
| Task<IReadOnlyCollection<IGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null); | |||
| Task<IGuildIntegration> CreateIntegrationAsync(ulong id, string type, RequestOptions options = null); | |||
| @@ -23,6 +23,8 @@ namespace Discord.API | |||
| public Optional<int> Position { get; set; } | |||
| [JsonProperty("permission_overwrites")] | |||
| public Optional<Overwrite[]> PermissionOverwrites { get; set; } | |||
| [JsonProperty("parent_id")] | |||
| public ulong? CategoryId { get; set; } | |||
| //TextChannel | |||
| [JsonProperty("topic")] | |||
| @@ -10,5 +10,7 @@ namespace Discord.API.Rest | |||
| public Optional<string> Name { get; set; } | |||
| [JsonProperty("position")] | |||
| public Optional<int> Position { get; set; } | |||
| [JsonProperty("parent_id")] | |||
| public Optional<ulong?> CategoryId { get; set; } | |||
| } | |||
| } | |||
| @@ -14,13 +14,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<Model> ModifyAsync(IGuildChannel channel, BaseDiscordClient client, | |||
| Action<GuildChannelProperties> func, | |||
| public static async Task<Model> ModifyAsync(IGuildChannel channel, BaseDiscordClient client, | |||
| Action<GuildChannelProperties> func, | |||
| RequestOptions options) | |||
| { | |||
| var args = new GuildChannelProperties(); | |||
| @@ -28,12 +28,13 @@ namespace Discord.Rest | |||
| var apiArgs = new API.Rest.ModifyGuildChannelParams | |||
| { | |||
| Name = args.Name, | |||
| Position = args.Position | |||
| Position = args.Position, | |||
| CategoryId = args.CategoryId | |||
| }; | |||
| return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | |||
| } | |||
| public static async Task<Model> ModifyAsync(ITextChannel channel, BaseDiscordClient client, | |||
| Action<TextChannelProperties> func, | |||
| public static async Task<Model> ModifyAsync(ITextChannel channel, BaseDiscordClient client, | |||
| Action<TextChannelProperties> func, | |||
| RequestOptions options) | |||
| { | |||
| var args = new TextChannelProperties(); | |||
| @@ -42,13 +43,14 @@ namespace Discord.Rest | |||
| { | |||
| Name = args.Name, | |||
| Position = args.Position, | |||
| CategoryId = args.CategoryId, | |||
| Topic = args.Topic, | |||
| IsNsfw = args.IsNsfw | |||
| }; | |||
| return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | |||
| } | |||
| public static async Task<Model> ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, | |||
| Action<VoiceChannelProperties> func, | |||
| public static async Task<Model> ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, | |||
| Action<VoiceChannelProperties> func, | |||
| RequestOptions options) | |||
| { | |||
| var args = new VoiceChannelProperties(); | |||
| @@ -58,6 +60,7 @@ namespace Discord.Rest | |||
| Bitrate = args.Bitrate, | |||
| Name = args.Name, | |||
| Position = args.Position, | |||
| CategoryId = args.CategoryId, | |||
| UserLimit = args.UserLimit.IsSpecified ? (args.UserLimit.Value ?? 0) : Optional.Create<int>() | |||
| }; | |||
| return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); | |||
| @@ -87,7 +90,7 @@ namespace Discord.Rest | |||
| } | |||
| //Messages | |||
| public static async Task<RestMessage> GetMessageAsync(IMessageChannel channel, BaseDiscordClient client, | |||
| public static async Task<RestMessage> GetMessageAsync(IMessageChannel channel, BaseDiscordClient client, | |||
| ulong id, RequestOptions options) | |||
| { | |||
| var guildId = (channel as IGuildChannel)?.GuildId; | |||
| @@ -98,7 +101,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<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, | |||
| public static IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(IMessageChannel channel, BaseDiscordClient client, | |||
| ulong? fromMessageId, Direction dir, int limit, RequestOptions options) | |||
| { | |||
| if (dir == Direction.Around) | |||
| @@ -124,7 +127,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(); | |||
| }, | |||
| @@ -180,7 +183,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(ITextChannel channel, BaseDiscordClient client, | |||
| IEnumerable<ulong> messageIds, RequestOptions options) | |||
| @@ -277,7 +280,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); | |||
| @@ -5,6 +5,7 @@ | |||
| Text = 0, | |||
| DM = 1, | |||
| Voice = 2, | |||
| Group = 3 | |||
| Group = 3, | |||
| Category = 4 | |||
| } | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| 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 RestCategoryChannel : RestGuildChannel, ICategoryChannel | |||
| { | |||
| internal RestCategoryChannel(BaseDiscordClient discord, IGuild guild, ulong id) | |||
| : base(discord, guild, id) | |||
| { | |||
| } | |||
| internal new static RestCategoryChannel Create(BaseDiscordClient discord, IGuild guild, Model model) | |||
| { | |||
| var entity = new RestCategoryChannel(discord, guild, model.Id); | |||
| entity.Update(model); | |||
| return entity; | |||
| } | |||
| private string DebuggerDisplay => $"{Name} ({Id}, Category)"; | |||
| // IGuildChannel | |||
| IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| //IChannel | |||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| } | |||
| } | |||
| @@ -16,7 +16,7 @@ namespace Discord.Rest | |||
| internal IGuild Guild { get; } | |||
| public string Name { get; private set; } | |||
| public int Position { get; private set; } | |||
| public ulong? CategoryId { get; private set; } | |||
| public ulong GuildId => Guild.Id; | |||
| internal RestGuildChannel(BaseDiscordClient discord, IGuild guild, ulong id) | |||
| @@ -32,6 +32,8 @@ namespace Discord.Rest | |||
| return RestTextChannel.Create(discord, guild, model); | |||
| case ChannelType.Voice: | |||
| return RestVoiceChannel.Create(discord, guild, model); | |||
| case ChannelType.Category: | |||
| return RestCategoryChannel.Create(discord, guild, model); | |||
| default: | |||
| // TODO: Channel categories | |||
| return new RestGuildChannel(discord, guild, model.Id); | |||
| @@ -61,7 +63,14 @@ namespace Discord.Rest | |||
| } | |||
| public Task DeleteAsync(RequestOptions options = null) | |||
| => ChannelHelper.DeleteAsync(this, Discord, options); | |||
| public async Task<ICategoryChannel> GetCategoryAsync() | |||
| { | |||
| if (CategoryId.HasValue) | |||
| return (await Guild.GetChannelAsync(CategoryId.Value).ConfigureAwait(false)) as ICategoryChannel; | |||
| return null; | |||
| } | |||
| public OverwritePermissions? GetPermissionOverwrite(IUser user) | |||
| { | |||
| for (int i = 0; i < _overwrites.Length; i++) | |||
| @@ -139,20 +148,20 @@ namespace Discord.Rest | |||
| => await GetInvitesAsync(options).ConfigureAwait(false); | |||
| async Task<IInviteMetadata> 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<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||
| => AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>(); //Overridden //Overridden in Text/Voice | |||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| @@ -157,6 +157,15 @@ namespace Discord.Rest | |||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | |||
| return RestVoiceChannel.Create(client, guild, model); | |||
| } | |||
| public static async Task<RestCategoryChannel> CreateCategoryChannelAsync(IGuild guild, BaseDiscordClient client, | |||
| string name, RequestOptions options) | |||
| { | |||
| if (name == null) throw new ArgumentNullException(nameof(name)); | |||
| var args = new CreateGuildChannelParams(name, ChannelType.Category); | |||
| var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); | |||
| return RestCategoryChannel.Create(client, guild, model); | |||
| } | |||
| //Integrations | |||
| public static async Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client, | |||
| @@ -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<GuildEmbedProperties> 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<IReadOnlyCollection<RestGuildChannel>> GetChannelsAsync(RequestOptions options = null) | |||
| => GuildHelper.GetChannelsAsync(this, Discord, options); | |||
| public Task<RestGuildChannel> GetChannelAsync(ulong id, RequestOptions options = null) | |||
| => GuildHelper.GetChannelAsync(this, Discord, id, options); | |||
| => GuildHelper.GetChannelAsync(this, Discord, id, options); | |||
| public async Task<RestTextChannel> 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<IReadOnlyCollection<RestCategoryChannel>> GetCategoryChannelsAsync(RequestOptions options = null) | |||
| { | |||
| var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); | |||
| return channels.Select(x => x as RestCategoryChannel).Where(x => x != null).ToImmutableArray(); | |||
| } | |||
| public async Task<RestVoiceChannel> GetAFKChannelAsync(RequestOptions options = null) | |||
| { | |||
| @@ -199,7 +204,7 @@ namespace Discord.Rest | |||
| public async Task<RestGuildChannel> 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; | |||
| } | |||
| @@ -217,6 +222,8 @@ namespace Discord.Rest | |||
| => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); | |||
| public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null) | |||
| => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); | |||
| public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null) | |||
| => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); | |||
| //Integrations | |||
| public Task<IReadOnlyCollection<RestGuildIntegration>> GetIntegrationsAsync(RequestOptions options = null) | |||
| @@ -236,7 +243,7 @@ namespace Discord.Rest | |||
| return null; | |||
| } | |||
| public async Task<RestRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), | |||
| public async Task<RestRole> 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); | |||
| @@ -320,6 +327,13 @@ namespace Discord.Rest | |||
| else | |||
| return ImmutableArray.Create<IVoiceChannel>(); | |||
| } | |||
| async Task<IReadOnlyCollection<ICategoryChannel>> IGuild.GetCategoriesAsync(CacheMode mode, RequestOptions options) | |||
| { | |||
| if (mode == CacheMode.AllowDownload) | |||
| return await GetCategoryChannelsAsync(options).ConfigureAwait(false); | |||
| else | |||
| return null; | |||
| } | |||
| async Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| { | |||
| if (mode == CacheMode.AllowDownload) | |||
| @@ -359,6 +373,8 @@ namespace Discord.Rest | |||
| => await CreateTextChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) | |||
| => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) | |||
| => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options) | |||
| => await GetIntegrationsAsync(options).ConfigureAwait(false); | |||
| @@ -368,7 +384,7 @@ namespace Discord.Rest | |||
| async Task<IReadOnlyCollection<IInviteMetadata>> IGuild.GetInvitesAsync(RequestOptions options) | |||
| => await GetInvitesAsync(options).ConfigureAwait(false); | |||
| IRole IGuild.GetRole(ulong id) | |||
| IRole IGuild.GetRole(ulong id) | |||
| => GetRole(id); | |||
| async Task<IRole> IGuild.CreateRoleAsync(string name, GuildPermissions? permissions, Color? color, bool isHoisted, RequestOptions options) | |||
| => await CreateRoleAsync(name, permissions, color, isHoisted, options).ConfigureAwait(false); | |||
| @@ -0,0 +1,36 @@ | |||
| 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 RpcCategoryChannel : RpcGuildChannel, ICategoryChannel | |||
| { | |||
| public IReadOnlyCollection<RpcMessage> CachedMessages { get; private set; } | |||
| public string Mention => MentionUtils.MentionChannel(Id); | |||
| internal RpcCategoryChannel(DiscordRpcClient discord, ulong id, ulong guildId) | |||
| : base(discord, id, guildId) | |||
| { | |||
| } | |||
| internal new static RpcCategoryChannel Create(DiscordRpcClient discord, Model model) | |||
| { | |||
| var entity = new RpcCategoryChannel(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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,53 @@ | |||
| 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 SocketCategoryChannel : SocketGuildChannel, ICategoryChannel | |||
| { | |||
| public override IReadOnlyCollection<SocketGuildUser> Users | |||
| => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); | |||
| public IReadOnlyCollection<SocketGuildChannel> Channels | |||
| => Guild.Channels.Where(x => x.CategoryId == CategoryId).ToImmutableArray(); | |||
| internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) | |||
| : base(discord, id, guild) | |||
| { | |||
| } | |||
| internal new static SocketCategoryChannel Create(SocketGuild guild, ClientState state, Model model) | |||
| { | |||
| var entity = new SocketCategoryChannel(guild.Discord, model.Id, guild); | |||
| entity.Update(state, model); | |||
| return entity; | |||
| } | |||
| private string DebuggerDisplay => $"{Name} ({Id}, Category)"; | |||
| internal new SocketCategoryChannel Clone() => MemberwiseClone() as SocketCategoryChannel; | |||
| // IGuildChannel | |||
| IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| //IChannel | |||
| IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| Task<IUser> IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| => throw new NotSupportedException(); | |||
| } | |||
| } | |||
| @@ -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? CategoryId { get; private set; } | |||
| public ICategoryChannel Category | |||
| => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; | |||
| public IReadOnlyCollection<Overwrite> PermissionOverwrites => _overwrites; | |||
| public new virtual IReadOnlyCollection<SocketGuildUser> Users => ImmutableArray.Create<SocketGuildUser>(); | |||
| @@ -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 SocketCategoryChannel.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; | |||
| CategoryId = model.CategoryId; | |||
| var overwrites = model.PermissionOverwrites.Value; | |||
| var newOverwrites = ImmutableArray.CreateBuilder<Overwrite>(overwrites.Length); | |||
| @@ -129,6 +135,9 @@ namespace Discord.WebSocket | |||
| IGuild IGuildChannel.Guild => Guild; | |||
| ulong IGuildChannel.GuildId => Guild.Id; | |||
| Task<ICategoryChannel> IGuildChannel.GetCategoryAsync() | |||
| => Task.FromResult(Category); | |||
| async Task<IReadOnlyCollection<IInviteMetadata>> IGuildChannel.GetInvitesAsync(RequestOptions options) | |||
| => await GetInvitesAsync(options).ConfigureAwait(false); | |||
| async Task<IInviteMetadata> IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) | |||
| @@ -75,7 +75,7 @@ namespace Discord.WebSocket | |||
| return id.HasValue ? GetVoiceChannel(id.Value) : null; | |||
| } | |||
| } | |||
| public SocketGuildChannel EmbedChannel | |||
| public SocketGuildChannel EmbedChannel | |||
| { | |||
| get | |||
| { | |||
| @@ -95,6 +95,8 @@ namespace Discord.WebSocket | |||
| => Channels.Select(x => x as SocketTextChannel).Where(x => x != null).ToImmutableArray(); | |||
| public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels | |||
| => Channels.Select(x => x as SocketVoiceChannel).Where(x => x != null).ToImmutableArray(); | |||
| public IReadOnlyCollection<SocketCategoryChannel> CategoryChannels | |||
| => Channels.Select(x => x as SocketCategoryChannel).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<SocketGuildChannel> Channels | |||
| @@ -317,6 +319,9 @@ namespace Discord.WebSocket | |||
| => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); | |||
| public Task<RestVoiceChannel> CreateVoiceChannelAsync(string name, RequestOptions options = null) | |||
| => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); | |||
| public Task<RestCategoryChannel> CreateCategoryChannelAsync(string name, RequestOptions options = null) | |||
| => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); | |||
| internal SocketGuildChannel AddChannel(ClientState state, ChannelModel model) | |||
| { | |||
| var channel = SocketGuildChannel.Create(this, state, model); | |||
| @@ -348,7 +353,7 @@ namespace Discord.WebSocket | |||
| return value; | |||
| return null; | |||
| } | |||
| public Task<RestRole> CreateRoleAsync(string name, GuildPermissions? permissions = default(GuildPermissions?), Color? color = default(Color?), | |||
| public Task<RestRole> 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) | |||
| @@ -594,7 +599,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) | |||
| { | |||
| @@ -651,6 +656,8 @@ namespace Discord.WebSocket | |||
| => Task.FromResult<ITextChannel>(GetTextChannel(id)); | |||
| Task<IReadOnlyCollection<IVoiceChannel>> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) | |||
| => Task.FromResult<IReadOnlyCollection<IVoiceChannel>>(VoiceChannels); | |||
| Task<IReadOnlyCollection<ICategoryChannel>> IGuild.GetCategoriesAsync(CacheMode mode , RequestOptions options) | |||
| => Task.FromResult<IReadOnlyCollection<ICategoryChannel>>(CategoryChannels); | |||
| Task<IVoiceChannel> IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) | |||
| => Task.FromResult<IVoiceChannel>(GetVoiceChannel(id)); | |||
| Task<IVoiceChannel> IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) | |||
| @@ -665,6 +672,8 @@ namespace Discord.WebSocket | |||
| => await CreateTextChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<IVoiceChannel> IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) | |||
| => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<ICategoryChannel> IGuild.CreateCategoryAsync(string name, RequestOptions options) | |||
| => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); | |||
| async Task<IReadOnlyCollection<IGuildIntegration>> IGuild.GetIntegrationsAsync(RequestOptions options) | |||
| => await GetIntegrationsAsync(options).ConfigureAwait(false); | |||