Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #995, Move Category Implementation from IGuildChannel to INestedChannel #1004

Merged
merged 20 commits into from May 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cafa537
Fix #995 ICategoryChannel.CategoryID throws NotSupportedException
Chris-Johnston Mar 27, 2018
0613b8d
Add tests
Chris-Johnston Mar 27, 2018
dca85f1
change run mode of TestChannelCategories
Chris-Johnston Mar 27, 2018
6ca9632
Add throw for GetCategoryAsync
Chris-Johnston Mar 27, 2018
f9c6707
Add xml doc explaining why exception is thrown
Chris-Johnston Mar 27, 2018
cb392db
Add test coverage for text and voice channel categories
Chris-Johnston Mar 27, 2018
8bb92cc
initial implementation of INestedChannel
Chris-Johnston Mar 28, 2018
de84fcf
more implementation of INestedChannel design
Chris-Johnston Mar 28, 2018
ba2a589
Add case in RestChannel Create for Category type
Chris-Johnston Mar 28, 2018
52739e4
set the CategoryID for RestVoiceChannel
Chris-Johnston Mar 30, 2018
bf7c4dc
rewrite channel category tests to work with existing pattern
Chris-Johnston Mar 30, 2018
3ed1148
remove outdated todo
Chris-Johnston Mar 30, 2018
bb51b66
Make IVoiceChannel implement INestedChannel
Chris-Johnston Apr 4, 2018
f6f8576
remove redundant interface implementation
Chris-Johnston Apr 4, 2018
252932c
Add c#7 feature from feedback
Chris-Johnston Apr 4, 2018
e9ef02e
Remove redundant GetCategoryAsync methods from socket entities
Chris-Johnston Apr 4, 2018
bb6b879
Added configureawait to async methods
Chris-Johnston Apr 4, 2018
cd37d63
change signature of interface GetCategoryAsync
Chris-Johnston Apr 4, 2018
36f49e1
Add check for cachemode in rest channel GetCategory
Chris-Johnston Apr 4, 2018
3cf417a
remove redundant IGuildChannel interface from ITextChannel and IVoice…
Chris-Johnston Apr 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Discord.Net.Core/Entities/Channels/ICategoryChannel.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand Down
8 changes: 2 additions & 6 deletions src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

Expand All @@ -9,10 +9,6 @@ public interface IGuildChannel : IChannel, IDeletable
/// <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>
Expand Down Expand Up @@ -49,4 +45,4 @@ public interface IGuildChannel : IChannel, IDeletable
/// <summary> Gets a user in this channel with the provided id.</summary>
new Task<IGuildUser> GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
}
}
}
16 changes: 16 additions & 0 deletions src/Discord.Net.Core/Entities/Channels/INestedChannel.cs
@@ -0,0 +1,16 @@
using System.Threading.Tasks;

namespace Discord
{
/// <summary>
/// A type of guild channel that can be nested within a category.
/// Contains a CategoryId that is set to the parent category, if it is set.
/// </summary>
public interface INestedChannel : IGuildChannel
{
/// <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, if it is set. If unset, returns null.</summary>
Task<ICategoryChannel> GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null);
}
}
6 changes: 3 additions & 3 deletions src/Discord.Net.Core/Entities/Channels/ITextChannel.cs
@@ -1,11 +1,11 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace Discord
{
public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel
public interface ITextChannel : IMessageChannel, IMentionable, INestedChannel
{
/// <summary> Checks if the channel is NSFW. </summary>
bool IsNsfw { get; }
Expand All @@ -28,4 +28,4 @@ public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel
/// <summary> Gets the webhooks for this text channel. </summary>
Task<IReadOnlyCollection<IWebhook>> GetWebhooksAsync(RequestOptions options = null);
}
}
}
6 changes: 3 additions & 3 deletions src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs
@@ -1,9 +1,9 @@
using System;
using System;
using System.Threading.Tasks;

namespace Discord
{
public interface IVoiceChannel : IGuildChannel, IAudioChannel
public interface IVoiceChannel : INestedChannel, IAudioChannel
{
/// <summary> Gets the bitrate, in bits per second, clients in this voice channel are requested to use. </summary>
int Bitrate { get; }
Expand All @@ -13,4 +13,4 @@ public interface IVoiceChannel : IGuildChannel, IAudioChannel
/// <summary> Modifies this voice channel. </summary>
Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null);
}
}
}
10 changes: 10 additions & 0 deletions src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs
Expand Up @@ -313,6 +313,16 @@ public static async Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(ITex
return models.Select(x => RestWebhook.Create(client, channel, x))
.ToImmutableArray();
}
// Categories
public static async Task<ICategoryChannel> GetCategoryAsync(INestedChannel channel, BaseDiscordClient client, RequestOptions options)
{
// if no category id specified, return null
if (!channel.CategoryId.HasValue)
return null;
// CategoryId will contain a value here
var model = await client.ApiClient.GetChannelAsync(channel.CategoryId.Value, options).ConfigureAwait(false);
return RestCategoryChannel.Create(client, model) as ICategoryChannel;
}

//Helpers
private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId)
Expand Down
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
Expand All @@ -25,10 +25,6 @@ internal new static RestCategoryChannel Create(BaseDiscordClient discord, IGuild
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)
Expand Down
4 changes: 3 additions & 1 deletion src/Discord.Net.Rest/Entities/Channels/RestChannel.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -24,6 +24,8 @@ internal static RestChannel Create(BaseDiscordClient discord, Model model)
case ChannelType.DM:
case ChannelType.Group:
return CreatePrivate(discord, model) as RestChannel;
case ChannelType.Category:
return RestCategoryChannel.Create(discord, new RestGuild(discord, model.GuildId.Value), model);
default:
return new RestChannel(discord, model.Id);
}
Expand Down
11 changes: 1 addition & 10 deletions src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
Expand All @@ -16,7 +16,6 @@ public class RestGuildChannel : RestChannel, IGuildChannel, IUpdateable
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)
Expand All @@ -35,7 +34,6 @@ internal static RestGuildChannel Create(BaseDiscordClient discord, IGuild guild,
case ChannelType.Category:
return RestCategoryChannel.Create(discord, guild, model);
default:
// TODO: Channel categories
return new RestGuildChannel(discord, guild, model.Id);
}
}
Expand Down Expand Up @@ -64,13 +62,6 @@ public async Task ModifyAsync(Action<GuildChannelProperties> func, RequestOption
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++)
Expand Down
23 changes: 18 additions & 5 deletions src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs
Expand Up @@ -12,6 +12,7 @@ namespace Discord.Rest
public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel
{
public string Topic { get; private set; }
public ulong? CategoryId { get; private set; }

public string Mention => MentionUtils.MentionChannel(Id);

Expand All @@ -31,7 +32,7 @@ internal new static RestTextChannel Create(BaseDiscordClient discord, IGuild gui
internal override void Update(Model model)
{
base.Update(model);

CategoryId = model.CategoryId;
Topic = model.Topic.Value;
_nsfw = model.Nsfw.GetValueOrDefault();
}
Expand All @@ -46,7 +47,7 @@ public Task<RestGuildUser> GetUserAsync(ulong id, RequestOptions options = null)
=> ChannelHelper.GetUserAsync(this, Guild, Discord, id, options);
public IAsyncEnumerable<IReadOnlyCollection<RestGuildUser>> GetUsersAsync(RequestOptions options = null)
=> ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options);

public Task<RestMessage> GetMessageAsync(ulong id, RequestOptions options = null)
=> ChannelHelper.GetMessageAsync(this, Discord, id, options);
public IAsyncEnumerable<IReadOnlyCollection<RestMessage>> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null)
Expand Down Expand Up @@ -83,16 +84,19 @@ public Task<RestWebhook> GetWebhookAsync(ulong id, RequestOptions options = null
=> ChannelHelper.GetWebhookAsync(this, Discord, id, options);
public Task<IReadOnlyCollection<RestWebhook>> GetWebhooksAsync(RequestOptions options = null)
=> ChannelHelper.GetWebhooksAsync(this, Discord, options);

public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);

private string DebuggerDisplay => $"{Name} ({Id}, Text)";

//ITextChannel
async Task<IWebhook> ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options)
=> await CreateWebhookAsync(name, avatar, options);
=> await CreateWebhookAsync(name, avatar, options).ConfigureAwait(false);
async Task<IWebhook> ITextChannel.GetWebhookAsync(ulong id, RequestOptions options)
=> await GetWebhookAsync(id, options);
=> await GetWebhookAsync(id, options).ConfigureAwait(false);
async Task<IReadOnlyCollection<IWebhook>> ITextChannel.GetWebhooksAsync(RequestOptions options)
=> await GetWebhooksAsync(options);
=> await GetWebhooksAsync(options).ConfigureAwait(false);

//IMessageChannel
async Task<IMessage> IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options)
Expand All @@ -109,6 +113,7 @@ IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync
else
return AsyncEnumerable.Empty<IReadOnlyCollection<IMessage>>();
}

IAsyncEnumerable<IReadOnlyCollection<IMessage>> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options)
{
if (mode == CacheMode.AllowDownload)
Expand Down Expand Up @@ -168,5 +173,13 @@ IAsyncEnumerable<IReadOnlyCollection<IUser>> IChannel.GetUsersAsync(CacheMode mo
else
return AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>();
}

// INestedChannel
async Task<ICategoryChannel> INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options)
{
Copy link
Member

@SubZero0 SubZero0 Apr 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this would be a better way (to not rely on the Guild on Rest):

if (mode == CacheMode.AllowDownload)
    return await GetCategoryAsync(options).ConfigureAwait(false);
else
    return null;

This is what I meant with "follow the same pattern as other rest entities and call the inner GetCategoryAsync" in my last review.
See:https://github.com/RogueException/Discord.Net/blob/dev/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs#L141

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is implemented, but isn't showing as outdated in Github.

if (CategoryId.HasValue && mode == CacheMode.AllowDownload)
return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel;
return null;
}
}
}
16 changes: 14 additions & 2 deletions src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs
@@ -1,4 +1,4 @@
using Discord.Audio;
using Discord.Audio;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -13,6 +13,7 @@ public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChann
{
public int Bitrate { get; private set; }
public int? UserLimit { get; private set; }
public ulong? CategoryId { get; private set; }

internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id)
: base(discord, guild, id)
Expand All @@ -27,7 +28,7 @@ internal new static RestVoiceChannel Create(BaseDiscordClient discord, IGuild gu
internal override void Update(Model model)
{
base.Update(model);

CategoryId = model.CategoryId;
Bitrate = model.Bitrate.Value;
UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null;
}
Expand All @@ -38,6 +39,9 @@ public async Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOption
Update(model);
}

public Task<ICategoryChannel> GetCategoryAsync(RequestOptions options = null)
=> ChannelHelper.GetCategoryAsync(this, Discord, options);

private string DebuggerDisplay => $"{Name} ({Id}, Voice)";

//IAudioChannel
Expand All @@ -48,5 +52,13 @@ Task<IGuildUser> IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOpt
=> Task.FromResult<IGuildUser>(null);
IAsyncEnumerable<IReadOnlyCollection<IGuildUser>> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options)
=> AsyncEnumerable.Empty<IReadOnlyCollection<IGuildUser>>();

// INestedChannel
async Task<ICategoryChannel> INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above.

if (CategoryId.HasValue && mode == CacheMode.AllowDownload)
return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel;
return null;
}
}
}
Expand Up @@ -20,7 +20,7 @@ public override IReadOnlyCollection<SocketGuildUser> Users
ChannelPermission.ViewChannel)).ToImmutableArray();

public IReadOnlyCollection<SocketGuildChannel> Channels
=> Guild.Channels.Where(x => x.CategoryId == Id).ToImmutableArray();
=> Guild.Channels.Where(x => x is INestedChannel nestedChannel && nestedChannel.CategoryId == Id).ToImmutableArray();

internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild)
: base(discord, id, guild)
Expand Down
13 changes: 3 additions & 10 deletions src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs
@@ -1,4 +1,4 @@
using Discord.Rest;
using Discord.Rest;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
Expand All @@ -16,10 +16,7 @@ public class SocketGuildChannel : SocketChannel, IGuildChannel

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 int Position { get; private set; }

public IReadOnlyCollection<Overwrite> PermissionOverwrites => _overwrites;
public new virtual IReadOnlyCollection<SocketGuildUser> Users => ImmutableArray.Create<SocketGuildUser>();
Expand Down Expand Up @@ -48,8 +45,7 @@ internal override void Update(ClientState state, Model model)
{
Name = model.Name.Value;
Position = model.Position.Value;
CategoryId = model.CategoryId;


var overwrites = model.PermissionOverwrites.Value;
var newOverwrites = ImmutableArray.CreateBuilder<Overwrite>(overwrites.Length);
for (int i = 0; i < overwrites.Length; i++)
Expand Down Expand Up @@ -135,9 +131,6 @@ public async Task<RestInviteMetadata> CreateInviteAsync(int? maxAge = 86400, int
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)
Expand Down
Expand Up @@ -16,6 +16,9 @@ public class SocketTextChannel : SocketGuildChannel, ITextChannel, ISocketMessag
private readonly MessageCache _messages;

public string Topic { get; private set; }
public ulong? CategoryId { get; private set; }
public ICategoryChannel Category
=> CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null;

private bool _nsfw;
public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this);
Expand All @@ -42,7 +45,7 @@ internal new static SocketTextChannel Create(SocketGuild guild, ClientState stat
internal override void Update(ClientState state, Model model)
{
base.Update(state, model);

CategoryId = model.CategoryId;
Topic = model.Topic.Value;
_nsfw = model.Nsfw.GetValueOrDefault();
}
Expand Down Expand Up @@ -164,5 +167,9 @@ async Task<IUserMessage> IMessageChannel.SendMessageAsync(string text, bool isTT
=> await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false);
IDisposable IMessageChannel.EnterTypingState(RequestOptions options)
=> EnterTypingState(options);

// INestedChannel
Task<ICategoryChannel> INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options)
=> Task.FromResult(Category);
}
}