From 838d60e2c2f72b7cd6de1133ff608e358003fed2 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 31 Oct 2016 19:50:38 -0400 Subject: [PATCH 001/263] Add API model for Reaction, implement REST methods for reactions --- src/Discord.Net.Core/API/Common/Reaction.cs | 20 +++++++++ .../API/DiscordRestApiClient.cs | 42 +++++++++++++++++++ .../API/Rest/GetReactionUsersParams.cs | 8 ++++ .../Entities/Messages/IUserMessage.cs | 13 +++++- src/Discord.Net.Core/Utils/Preconditions.cs | 1 + .../Entities/Messages/MessageHelper.cs | 23 ++++++++++ .../Entities/Messages/RestUserMessage.cs | 14 +++++++ .../Entities/Messages/RpcUserMessage.cs | 13 ++++++ .../Entities/Messages/SocketUserMessage.cs | 13 ++++++ 9 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/Discord.Net.Core/API/Common/Reaction.cs create mode 100644 src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs diff --git a/src/Discord.Net.Core/API/Common/Reaction.cs b/src/Discord.Net.Core/API/Common/Reaction.cs new file mode 100644 index 000000000..69dbf4b0c --- /dev/null +++ b/src/Discord.Net.Core/API/Common/Reaction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Discord.API.Common +{ + public class Reaction + { + [JsonProperty("user_id")] + public ulong UserId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + [JsonProperty("emoji")] + public Emoji Emoji { get; set; } + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index ac18e8ace..4d85c68b2 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -512,6 +512,48 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("PATCH", () => $"channels/{channelId}/messages/{messageId}", args, ids, clientBucketId: ClientBucket.SendEditId, options: options).ConfigureAwait(false); } + public async Task AddReactionAsync(ulong channelId, ulong messageId, string emoji, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("PUT", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/@me", ids, options: options).ConfigureAwait(false); + } + public async Task RemoveReactionAsync(ulong channelId, ulong messageId, ulong userId, string emoji, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/{userId}", ids, options: options).ConfigureAwait(false); + } + public async Task> GetReactionUsersAsync(ulong channelId, ulong messageId, string emoji, GetReactionUsersParams args, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + Preconditions.NotNullOrWhitespace(emoji, nameof(emoji)); + Preconditions.NotNull(args, nameof(args)); + Preconditions.GreaterThan(args.Limit, 0, nameof(args.Limit)); + Preconditions.AtMost(args.Limit, DiscordConfig.MaxUsersPerBatch, nameof(args.Limit)); + Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); + options = RequestOptions.CreateOrClone(options); + + int limit = args.Limit.GetValueOrDefault(int.MaxValue); + ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); + + var ids = new BucketIds(channelId: channelId); + Expression> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}"; + return await SendAsync>("GET", endpoint, ids, options: options).ConfigureAwait(false); + } public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); diff --git a/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs new file mode 100644 index 000000000..bb9b22ab8 --- /dev/null +++ b/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs @@ -0,0 +1,8 @@ +namespace Discord.API.Rest +{ + public class GetReactionUsersParams + { + public Optional Limit { get; set; } + public Optional AfterUserId { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index a9dc4735c..3a7b5821a 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -1,5 +1,6 @@ using Discord.API.Rest; using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace Discord @@ -12,7 +13,17 @@ namespace Discord Task PinAsync(RequestOptions options = null); /// Removes this message from its channel's pinned messages. Task UnpinAsync(RequestOptions options = null); - + + /// Adds a reaction to this message. + Task AddReactionAsync(Emoji emoji, RequestOptions options = null); + /// Adds a reaction to this message. + Task AddReactionAsync(string emoji, RequestOptions options = null); + /// Removes a reaction from message. + Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); + /// Removes a reaction from this message. + Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); + Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options = null); + /// Transforms this message's text into a human readable form by resolving its tags. string Resolve( TagHandling userHandling = TagHandling.Name, diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 14c9db24d..2c6ea2417 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -45,6 +45,7 @@ namespace Discord throw new ArgumentException("Argument cannot be blank.", name); } } + //Numerics public static void NotEqual(sbyte obj, sbyte value, string name) { if (obj == value) throw new ArgumentOutOfRangeException(name); } diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 358f6f5a9..602a790a5 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -23,6 +23,29 @@ namespace Discord.Rest await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); } + public static async Task AddReactionAsync(IMessage msg, Emoji emoji, BaseDiscordClient client, RequestOptions options) + => await AddReactionAsync(msg, $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); + public static async Task AddReactionAsync(IMessage msg, string emoji, BaseDiscordClient client, RequestOptions options) + { + await client.ApiClient.AddReactionAsync(msg.Channel.Id, msg.Id, emoji, options).ConfigureAwait(false); + } + + public static async Task RemoveReactionAsync(IMessage msg, IUser user, Emoji emoji, BaseDiscordClient client, RequestOptions options) + => await RemoveReactionAsync(msg, user, $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); + public static async Task RemoveReactionAsync(IMessage msg, IUser user, string emoji, BaseDiscordClient client, + RequestOptions options) + { + await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options); + } + + public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, + Action func, BaseDiscordClient client, RequestOptions options) + { + var args = new GetReactionUsersParams(); + func(args); + return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => u as IUser).Where(u => u != null).ToImmutableArray(); + } + public static async Task PinAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index d3d3b6fce..66d270fdd 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -117,6 +117,20 @@ namespace Discord.Rest Update(model); } + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + + public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index edfa60484..01cff3611 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -101,6 +101,19 @@ namespace Discord.Rpc public Task ModifyAsync(Action func, RequestOptions options) => MessageHelper.ModifyAsync(this, Discord, func, options); + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index db9b82ebd..4174ff899 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -113,6 +113,19 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); + public Task AddReactionAsync(Emoji emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + public Task AddReactionAsync(string emoji, RequestOptions options) + => MessageHelper.AddReactionAsync(this, emoji, Discord, options); + + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) + => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); public Task UnpinAsync(RequestOptions options = null) From 7018bc9c589dfa507cc8b96e9f28b195a9a9c452 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 31 Oct 2016 21:00:35 -0400 Subject: [PATCH 002/263] """Support""" the 'reactions' field on message objects this is all really broken --- src/Discord.Net.Core/API/Common/Message.cs | 2 ++ src/Discord.Net.Core/API/Common/Reaction.cs | 12 ++++----- .../Entities/Messages/IMessage.cs | 2 ++ .../Entities/Messages/IReaction.cs | 12 +++++++++ .../Entities/Messages/RestMessage.cs | 2 ++ .../Entities/Messages/RestReaction.cs | 26 +++++++++++++++++++ .../Entities/Messages/RestUserMessage.cs | 16 ++++++++++++ .../Entities/Messages/RpcMessage.cs | 1 + .../API/Gateway/GatewayReaction.cs | 20 ++++++++++++++ .../Entities/Messages/SocketMessage.cs | 1 + .../Entities/Messages/SocketReaction.cs | 24 +++++++++++++++++ .../Entities/Messages/SocketUserMessage.cs | 17 ++++++++++++ 12 files changed, 128 insertions(+), 7 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Messages/IReaction.cs create mode 100644 src/Discord.Net.Rest/Entities/Messages/RestReaction.cs create mode 100644 src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs create mode 100644 src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs diff --git a/src/Discord.Net.Core/API/Common/Message.cs b/src/Discord.Net.Core/API/Common/Message.cs index 2c19780b1..f812d0622 100644 --- a/src/Discord.Net.Core/API/Common/Message.cs +++ b/src/Discord.Net.Core/API/Common/Message.cs @@ -36,5 +36,7 @@ namespace Discord.API public Optional Embeds { get; set; } [JsonProperty("pinned")] public Optional Pinned { get; set; } + [JsonProperty("reactions")] + public Optional Reactions { get; set; } } } diff --git a/src/Discord.Net.Core/API/Common/Reaction.cs b/src/Discord.Net.Core/API/Common/Reaction.cs index 69dbf4b0c..e143004ef 100644 --- a/src/Discord.Net.Core/API/Common/Reaction.cs +++ b/src/Discord.Net.Core/API/Common/Reaction.cs @@ -4,17 +4,15 @@ using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json; -namespace Discord.API.Common +namespace Discord.API { public class Reaction { - [JsonProperty("user_id")] - public ulong UserId { get; set; } - [JsonProperty("message_id")] - public ulong MessageId { get; set; } + [JsonProperty("count")] + public int Count { get; set; } + [JsonProperty("me")] + public bool Me { get; set; } [JsonProperty("emoji")] public Emoji Emoji { get; set; } - [JsonProperty("channel_id")] - public ulong ChannelId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 6bb44368b..13f3b662a 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -31,6 +31,8 @@ namespace Discord IReadOnlyCollection Attachments { get; } /// Returns all embeds included in this message. IReadOnlyCollection Embeds { get; } + /// Returns all reactions included in this message. + IReadOnlyCollection Reactions { get; } /// Returns all tags included in this message's content. IReadOnlyCollection Tags { get; } /// Returns the ids of channels mentioned in this message. diff --git a/src/Discord.Net.Core/Entities/Messages/IReaction.cs b/src/Discord.Net.Core/Entities/Messages/IReaction.cs new file mode 100644 index 000000000..5d7eae44e --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/IReaction.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Discord +{ + public interface IReaction + { + API.Emoji Emoji { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index ea064dd81..41cff4380 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -27,6 +27,7 @@ namespace Discord.Rest public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; @@ -70,5 +71,6 @@ namespace Discord.Rest IReadOnlyCollection IMessage.Attachments => Attachments; IReadOnlyCollection IMessage.Embeds => Embeds; IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); + IReadOnlyCollection IMessage.Reactions => Reactions; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs new file mode 100644 index 000000000..2fb534ce4 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Reaction; + +namespace Discord +{ + public class RestReaction : IReaction + { + internal RestReaction(Model model) + { + _emoji = model.Emoji; + _count = model.Count; + + } + + internal readonly API.Emoji _emoji; + internal readonly int _count; + internal readonly bool _me; + + public API.Emoji Emoji => _emoji; + public int Count => _count; + public bool Me => _me; + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 66d270fdd..2625718c7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -18,6 +18,7 @@ namespace Discord.Rest private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; + private ImmutableArray _reactions; public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -29,6 +30,7 @@ namespace Discord.Rest public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; + public override IReadOnlyCollection Reactions => _reactions; internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, RestUser author, IGuild guild) : base(discord, id, channel, author, guild) @@ -103,6 +105,20 @@ namespace Discord.Rest } } + if (model.Reactions.IsSpecified) + { + var value = model.Reactions.Value; + if (value.Length > 0) + { + var reactions = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + reactions.Add(new RestReaction(value[i])); + _reactions = reactions.ToImmutable(); + } + else + _reactions = ImmutableArray.Create(); + } + if (model.Content.IsSpecified) { var text = model.Content.Value; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index d0464487b..35e4ef52e 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -28,6 +28,7 @@ namespace Discord.Rpc public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUserIds => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs new file mode 100644 index 000000000..096159478 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class GatewayReaction : Reaction + { + [JsonProperty("user_id")] + public ulong UserId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + [JsonProperty("emoji")] + public Discord.API.Emoji Emoji { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 0b09d2d22..3f7512295 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -27,6 +27,7 @@ namespace Discord.WebSocket public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); + public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs new file mode 100644 index 000000000..b96513cc1 --- /dev/null +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Model = Discord.API.Gateway.GatewayReaction; + +namespace Discord.WebSocket +{ + public class SocketReaction : IReaction + { + internal SocketReaction(Model model) + { + UserId = model.UserId; + MessageId = model.MessageId; + ChannelId = model.ChannelId; + Emoji = model.Emoji; + } + + public ulong UserId { get; internal set; } + public ulong MessageId { get; internal set; } + public ulong ChannelId { get; internal set; } + public API.Emoji Emoji { get; internal set; } + } +} diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 4174ff899..5ccf1825f 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; +using Discord.API.Gateway; using Model = Discord.API.Message; namespace Discord.WebSocket @@ -18,6 +19,7 @@ namespace Discord.WebSocket private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; + private ImmutableArray _reactions; public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -29,6 +31,7 @@ namespace Discord.WebSocket public override IReadOnlyCollection MentionedChannels => MessageHelper.FilterTagsByValue(TagType.ChannelMention, _tags); public override IReadOnlyCollection MentionedRoles => MessageHelper.FilterTagsByValue(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); + public override IReadOnlyCollection Reactions => _reactions; internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) : base(discord, id, channel, author) @@ -101,6 +104,20 @@ namespace Discord.WebSocket } } + if (model.Reactions.IsSpecified) + { + var value = model.Reactions.Value; + if (value.Length > 0) + { + var reactions = ImmutableArray.CreateBuilder(value.Length); + for (int i = 0; i < value.Length; i++) + reactions.Add(new SocketReaction(value[i] as GatewayReaction)); + _reactions = reactions.ToImmutable(); + } + else + _reactions = ImmutableArray.Create(); + } + if (model.Content.IsSpecified) { var text = model.Content.Value; From e2e2c4308dde87105c6f33c7722f2ddee67e389f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 18:15:47 -0400 Subject: [PATCH 003/263] Refactor, rearrange, reimplement reactions; receive over gateway --- .../Entities/Messages/Emoji.cs | 5 ++ .../Entities/Messages/IMessage.cs | 2 - .../Entities/Messages/IReaction.cs | 2 +- .../Entities/Messages/IUserMessage.cs | 3 ++ .../Entities/Messages/RestMessage.cs | 2 - .../Entities/Messages/RestReaction.cs | 16 +++---- .../Entities/Messages/RestUserMessage.cs | 2 +- .../Entities/Messages/RpcMessage.cs | 1 - .../Entities/Messages/RpcUserMessage.cs | 1 + .../API/Gateway/GatewayReaction.cs | 4 +- .../DiscordSocketClient.Events.cs | 12 +++++ .../DiscordSocketClient.cs | 48 +++++++++++++++++++ .../Entities/Messages/SocketMessage.cs | 1 - .../Entities/Messages/SocketReaction.cs | 10 ++-- .../Entities/Messages/SocketUserMessage.cs | 28 +++++------ 15 files changed, 96 insertions(+), 41 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index 612e99f29..fc71e944b 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -19,6 +19,11 @@ namespace Discord Name = name; } + internal static Emoji FromApi(API.Emoji emoji) + { + return new Emoji(emoji.Id, emoji.Name); + } + public static Emoji Parse(string text) { Emoji result; diff --git a/src/Discord.Net.Core/Entities/Messages/IMessage.cs b/src/Discord.Net.Core/Entities/Messages/IMessage.cs index 13f3b662a..6bb44368b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IMessage.cs @@ -31,8 +31,6 @@ namespace Discord IReadOnlyCollection Attachments { get; } /// Returns all embeds included in this message. IReadOnlyCollection Embeds { get; } - /// Returns all reactions included in this message. - IReadOnlyCollection Reactions { get; } /// Returns all tags included in this message's content. IReadOnlyCollection Tags { get; } /// Returns the ids of channels mentioned in this message. diff --git a/src/Discord.Net.Core/Entities/Messages/IReaction.cs b/src/Discord.Net.Core/Entities/Messages/IReaction.cs index 5d7eae44e..66832760b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IReaction.cs +++ b/src/Discord.Net.Core/Entities/Messages/IReaction.cs @@ -7,6 +7,6 @@ namespace Discord { public interface IReaction { - API.Emoji Emoji { get; } + Emoji Emoji { get; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 3a7b5821a..5b6ab2773 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -14,6 +14,9 @@ namespace Discord /// Removes this message from its channel's pinned messages. Task UnpinAsync(RequestOptions options = null); + /// Returns all reactions included in this message. + IReadOnlyDictionary Reactions { get; } + /// Adds a reaction to this message. Task AddReactionAsync(Emoji emoji, RequestOptions options = null); /// Adds a reaction to this message. diff --git a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs index 41cff4380..ea064dd81 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestMessage.cs @@ -27,7 +27,6 @@ namespace Discord.Rest public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; @@ -71,6 +70,5 @@ namespace Discord.Rest IReadOnlyCollection IMessage.Attachments => Attachments; IReadOnlyCollection IMessage.Embeds => Embeds; IReadOnlyCollection IMessage.MentionedUserIds => MentionedUsers.Select(x => x.Id).ToImmutableArray(); - IReadOnlyCollection IMessage.Reactions => Reactions; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs index 2fb534ce4..7512dd4d8 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -10,17 +10,13 @@ namespace Discord { internal RestReaction(Model model) { - _emoji = model.Emoji; - _count = model.Count; - + Emoji = Emoji.FromApi(model.Emoji); + Count = model.Count; + Me = model.Me; } - internal readonly API.Emoji _emoji; - internal readonly int _count; - internal readonly bool _me; - - public API.Emoji Emoji => _emoji; - public int Count => _count; - public bool Me => _me; + public Emoji Emoji { get; private set; } + public int Count { get; private set; } + public bool Me { get; private set; } } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 2625718c7..1af844501 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -30,7 +30,7 @@ namespace Discord.Rest public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; - public override IReadOnlyCollection Reactions => _reactions; + public IReadOnlyDictionary Reactions => _reactions.ToDictionary(x => x.Emoji, x => x.Count); internal RestUserMessage(BaseDiscordClient discord, ulong id, IMessageChannel channel, RestUser author, IGuild guild) : base(discord, id, channel, author, guild) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index 35e4ef52e..d0464487b 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -28,7 +28,6 @@ namespace Discord.Rpc public virtual IReadOnlyCollection MentionedRoleIds => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUserIds => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 01cff3611..832e54d81 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -30,6 +30,7 @@ namespace Discord.Rpc public override IReadOnlyCollection MentionedRoleIds => MessageHelper.FilterTagsByKey(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUserIds => MessageHelper.FilterTagsByKey(TagType.UserMention, _tags); public override IReadOnlyCollection Tags => _tags; + public IReadOnlyDictionary Reactions => ImmutableDictionary.Create(); internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) : base(discord, id, channel, author) diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs index 096159478..2b9d6becc 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GatewayReaction : Reaction + public class GatewayReaction { [JsonProperty("user_id")] public ulong UserId { get; set; } @@ -15,6 +15,6 @@ namespace Discord.API.Gateway [JsonProperty("channel_id")] public ulong ChannelId { get; set; } [JsonProperty("emoji")] - public Discord.API.Emoji Emoji { get; set; } + public Emoji Emoji { get; set; } } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 529caaa87..4454e619f 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -71,6 +71,18 @@ namespace Discord.WebSocket remove { _messageUpdatedEvent.Remove(value); } } private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); + public event Func, SocketReaction, Task> ReactionAdded + { + add { _reactionAddedEvent.Add(value); } + remove { _reactionAddedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, SocketReaction, Task> ReactionRemoved + { + add { _reactionRemovedEvent.Add(value); } + remove { _reactionRemovedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 3eb4158d1..2a70824d4 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1304,6 +1304,54 @@ namespace Discord.WebSocket } } break; + case "MESSAGE_REACTION_ADD": + { + await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + SocketReaction reaction = new SocketReaction(data); + if (cachedMsg != null) + { + cachedMsg.AddReaction(reaction); + await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + } + await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_ADD referenced an unknown channel.").ConfigureAwait(false); + return; + } + break; + } + case "MESSAGE_REACTION_REMOVE": + { + await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + SocketReaction reaction = new SocketReaction(data); + if (cachedMsg != null) + { + cachedMsg.RemoveReaction(reaction); + await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + } + await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); + return; + } + break; + } case "MESSAGE_DELETE_BULK": { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs index 3f7512295..0b09d2d22 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketMessage.cs @@ -27,7 +27,6 @@ namespace Discord.WebSocket public virtual IReadOnlyCollection MentionedRoles => ImmutableArray.Create(); public virtual IReadOnlyCollection MentionedUsers => ImmutableArray.Create(); public virtual IReadOnlyCollection Tags => ImmutableArray.Create(); - public virtual IReadOnlyCollection Reactions => ImmutableArray.Create(); public virtual ulong? WebhookId => null; public bool IsWebhook => WebhookId != null; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index b96513cc1..0ac8c4342 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -13,12 +13,12 @@ namespace Discord.WebSocket UserId = model.UserId; MessageId = model.MessageId; ChannelId = model.ChannelId; - Emoji = model.Emoji; + Emoji = Emoji.FromApi(model.Emoji); } - public ulong UserId { get; internal set; } - public ulong MessageId { get; internal set; } - public ulong ChannelId { get; internal set; } - public API.Emoji Emoji { get; internal set; } + public ulong UserId { get; private set; } + public ulong MessageId { get; private set; } + public ulong ChannelId { get; private set; } + public Emoji Emoji { get; private set; } } } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 5ccf1825f..957fa64b3 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading.Tasks; +using System.Linq; using Discord.API.Gateway; using Model = Discord.API.Message; @@ -19,7 +20,7 @@ namespace Discord.WebSocket private ImmutableArray _attachments; private ImmutableArray _embeds; private ImmutableArray _tags; - private ImmutableArray _reactions; + private List _reactions = new List(); public override bool IsTTS => _isTTS; public override bool IsPinned => _isPinned; @@ -31,7 +32,7 @@ namespace Discord.WebSocket public override IReadOnlyCollection MentionedChannels => MessageHelper.FilterTagsByValue(TagType.ChannelMention, _tags); public override IReadOnlyCollection MentionedRoles => MessageHelper.FilterTagsByValue(TagType.RoleMention, _tags); public override IReadOnlyCollection MentionedUsers => MessageHelper.FilterTagsByValue(TagType.UserMention, _tags); - public override IReadOnlyCollection Reactions => _reactions; + public IReadOnlyDictionary Reactions => _reactions.GroupBy(r => r.Emoji).ToDictionary(x => x.Key, x => x.Count()); internal SocketUserMessage(DiscordSocketClient discord, ulong id, ISocketMessageChannel channel, SocketUser author) : base(discord, id, channel, author) @@ -104,20 +105,6 @@ namespace Discord.WebSocket } } - if (model.Reactions.IsSpecified) - { - var value = model.Reactions.Value; - if (value.Length > 0) - { - var reactions = ImmutableArray.CreateBuilder(value.Length); - for (int i = 0; i < value.Length; i++) - reactions.Add(new SocketReaction(value[i] as GatewayReaction)); - _reactions = reactions.ToImmutable(); - } - else - _reactions = ImmutableArray.Create(); - } - if (model.Content.IsSpecified) { var text = model.Content.Value; @@ -126,6 +113,15 @@ namespace Discord.WebSocket model.Content = text; } } + internal void AddReaction(SocketReaction reaction) + { + _reactions.Add(reaction); + } + internal void RemoveReaction(SocketReaction reaction) + { + if (_reactions.Contains(reaction)) + _reactions.Remove(reaction); + } public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); From c6d9bbf06373ca04a0f8ce0c55a9ad6f219c6fa4 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 18:45:35 -0400 Subject: [PATCH 004/263] Clean up some bugs when parsing unicode emoji --- src/Discord.Net.Core/API/Common/Emoji.cs | 2 +- src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs | 2 +- src/Discord.Net.Core/Entities/Messages/Emoji.cs | 2 +- src/Discord.Net.Core/Entities/Messages/IUserMessage.cs | 2 +- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 4 ++-- src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs | 4 ++-- .../Entities/Messages/SocketUserMessage.cs | 6 +++--- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Discord.Net.Core/API/Common/Emoji.cs b/src/Discord.Net.Core/API/Common/Emoji.cs index 032ae51eb..c04786039 100644 --- a/src/Discord.Net.Core/API/Common/Emoji.cs +++ b/src/Discord.Net.Core/API/Common/Emoji.cs @@ -6,7 +6,7 @@ namespace Discord.API public class Emoji { [JsonProperty("id")] - public ulong Id { get; set; } + public ulong? Id { get; set; } [JsonProperty("name")] public string Name { get; set; } [JsonProperty("roles")] diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs index 8b2bbd9c2..94f79afa5 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs @@ -24,7 +24,7 @@ namespace Discord } internal static GuildEmoji Create(Model model) { - return new GuildEmoji(model.Id, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); + return new GuildEmoji(model.Id.Value, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); } public override string ToString() => Name; diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index fc71e944b..dddbe65f1 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -21,7 +21,7 @@ namespace Discord internal static Emoji FromApi(API.Emoji emoji) { - return new Emoji(emoji.Id, emoji.Name); + return new Emoji(emoji.Id.GetValueOrDefault(), emoji.Name); } public static Emoji Parse(string text) diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 5b6ab2773..5fe0ccbea 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -25,7 +25,7 @@ namespace Discord Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); /// Removes a reaction from this message. Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); - Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options = null); + Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); /// Transforms this message's text into a human readable form by resolving its tags. string Resolve( diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 1af844501..bc70e608e 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -143,8 +143,8 @@ namespace Discord.Rest public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 832e54d81..20129054f 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -112,8 +112,8 @@ namespace Discord.Rpc public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options) => MessageHelper.PinAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 957fa64b3..4d097fb32 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -135,9 +135,9 @@ namespace Discord.WebSocket => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - - public Task> GetReactionUsersAsync(string emoji, Action func, RequestOptions options) - => MessageHelper.GetReactionUsersAsync(this, emoji, func, Discord, options); + + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); From 718560917c240a9a17068943c96eb2150b3d4103 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 21:43:18 -0500 Subject: [PATCH 005/263] RequestOptions should be optional on Reactions --- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 8 ++++---- src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs | 8 ++++---- .../Entities/Messages/SocketUserMessage.cs | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index bc70e608e..1313235a2 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -133,14 +133,14 @@ namespace Discord.Rest Update(model); } - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 20129054f..b2e6b40e3 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -102,14 +102,14 @@ namespace Discord.Rpc public Task ModifyAsync(Action func, RequestOptions options) => MessageHelper.ModifyAsync(this, Discord, func, options); - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 4d097fb32..3205f2d7a 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -126,14 +126,14 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); - public Task AddReactionAsync(Emoji emoji, RequestOptions options) + public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task AddReactionAsync(string emoji, RequestOptions options) + public Task AddReactionAsync(string emoji, RequestOptions options = null) => MessageHelper.AddReactionAsync(this, emoji, Discord, options); - public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); - public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options) + public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) From 676fbbcd236cd2c85c4baf1b0c7861a57c7c4e8e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 5 Nov 2016 22:23:01 -0500 Subject: [PATCH 006/263] Fix invoking reaction events twice --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 2a70824d4..53deeffef 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1318,6 +1318,7 @@ namespace Discord.WebSocket { cachedMsg.AddReaction(reaction); await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + return; } await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); } @@ -1342,6 +1343,7 @@ namespace Discord.WebSocket { cachedMsg.RemoveReaction(reaction); await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); + return; } await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); } From 8dfc6aea67de6f296b69f73ee0325d134cecde48 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 6 Nov 2016 13:33:37 -0500 Subject: [PATCH 007/263] Add optional user/message/channel objects to Reactions --- .../Entities/Messages/MessageHelper.cs | 2 +- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 7 +++++-- .../Entities/Messages/SocketReaction.cs | 12 ++++++++---- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 602a790a5..8caaa2532 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -35,7 +35,7 @@ namespace Discord.Rest public static async Task RemoveReactionAsync(IMessage msg, IUser user, string emoji, BaseDiscordClient client, RequestOptions options) { - await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options); + await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options).ConfigureAwait(false); } public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 53deeffef..238cdbcb1 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1313,7 +1313,9 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - SocketReaction reaction = new SocketReaction(data); + var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); + SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); + if (cachedMsg != null) { cachedMsg.AddReaction(reaction); @@ -1338,7 +1340,8 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - SocketReaction reaction = new SocketReaction(data); + var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); + SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); if (cachedMsg != null) { cachedMsg.RemoveReaction(reaction); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index 0ac8c4342..c2e544a2c 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -8,17 +8,21 @@ namespace Discord.WebSocket { public class SocketReaction : IReaction { - internal SocketReaction(Model model) + internal SocketReaction(Model model, ISocketMessageChannel channel, Optional message, Optional user) { - UserId = model.UserId; + Channel = channel; + Message = message; MessageId = model.MessageId; - ChannelId = model.ChannelId; + User = user; + UserId = model.UserId; Emoji = Emoji.FromApi(model.Emoji); } public ulong UserId { get; private set; } + public Optional User { get; private set; } public ulong MessageId { get; private set; } - public ulong ChannelId { get; private set; } + public Optional Message { get; private set; } + public ISocketMessageChannel Channel { get; private set; } public Emoji Emoji { get; private set; } } } From 9725dcec24bb004d1effa0f1d5e29c1d4b1814f4 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 6 Nov 2016 13:34:38 -0500 Subject: [PATCH 008/263] Use ToString in converter instead of boxing-cast In cases where Discord sent a value of `id=0`, this would throw an invalid-cast, where 0u64 cannot be cast to string. --- src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs index a2e409292..fa22da656 100644 --- a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs +++ b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs @@ -16,7 +16,7 @@ namespace Discord.Net.Converters { object value = reader.Value; if (value != null) - return ulong.Parse((string)value, NumberStyles.None, CultureInfo.InvariantCulture); + return ulong.Parse(value.ToString(), NumberStyles.None, CultureInfo.InvariantCulture); else return null; } From 6b5a4b3ee69709e9396af14a777a787b362f1c16 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 11 Nov 2016 15:52:14 -0500 Subject: [PATCH 009/263] Support the "Clear Reactions" endpoint --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 11 +++++++++++ .../Entities/Messages/IUserMessage.cs | 2 ++ .../Entities/Messages/MessageHelper.cs | 5 +++++ .../Entities/Messages/RestUserMessage.cs | 3 +++ .../Entities/Messages/RpcUserMessage.cs | 3 +++ .../Entities/Messages/SocketUserMessage.cs | 3 +++ 6 files changed, 27 insertions(+) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index 4d85c68b2..6b5cbb484 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -536,6 +536,17 @@ namespace Discord.API await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}/{userId}", ids, options: options).ConfigureAwait(false); } + public async Task RemoveAllReactionsAsync(ulong channelId, ulong messageId, RequestOptions options = null) + { + Preconditions.NotEqual(channelId, 0, nameof(channelId)); + Preconditions.NotEqual(messageId, 0, nameof(messageId)); + + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(channelId: channelId); + + await SendAsync("DELETE", () => $"channels/{channelId}/messages/{messageId}/reactions", ids, options: options).ConfigureAwait(false); + } public async Task> GetReactionUsersAsync(ulong channelId, ulong messageId, string emoji, GetReactionUsersParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 5fe0ccbea..ba2a00bd8 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -25,6 +25,8 @@ namespace Discord Task RemoveReactionAsync(Emoji emoji, IUser user, RequestOptions options = null); /// Removes a reaction from this message. Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null); + /// Removes all reactions from this message. + Task RemoveAllReactionsAsync(RequestOptions options = null); Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); /// Transforms this message's text into a human readable form by resolving its tags. diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 8caaa2532..33df3631c 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -38,6 +38,11 @@ namespace Discord.Rest await client.ApiClient.RemoveReactionAsync(msg.Channel.Id, msg.Id, user.Id, emoji, options).ConfigureAwait(false); } + public static async Task RemoveAllReactionsAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) + { + await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options); + } + public static async Task> GetReactionUsersAsync(IMessage msg, string emoji, Action func, BaseDiscordClient client, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 1313235a2..dde1c9d34 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -142,6 +142,9 @@ namespace Discord.Rest => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index b2e6b40e3..476a1ed25 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -111,6 +111,9 @@ namespace Discord.Rpc => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); public Task> GetReactionUsersAsync(string emoji, int limit, ulong? afterUserId, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 3205f2d7a..43e61f670 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -136,6 +136,9 @@ namespace Discord.WebSocket public Task RemoveReactionAsync(string emoji, IUser user, RequestOptions options = null) => MessageHelper.RemoveReactionAsync(this, user, emoji, Discord, options); + public Task RemoveAllReactionsAsync(RequestOptions options = null) + => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); + public Task> GetReactionUsersAsync(string emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); From 983b50120daa5d9d472e889b2df8e42c9ef69d18 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 11 Nov 2016 16:28:07 -0500 Subject: [PATCH 010/263] Support "MESSAGE_REACTION_REMOVE_ALL" dispatch --- .../API/Gateway/RemoveAllReactionsEvent.cs | 12 ++++++++ .../DiscordSocketClient.Events.cs | 6 ++++ .../DiscordSocketClient.cs | 29 +++++++++++++++++-- .../Entities/Messages/SocketUserMessage.cs | 4 +++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs new file mode 100644 index 000000000..944a6e7c9 --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.API.Gateway +{ + public class RemoveAllReactionsEvent + { + [JsonProperty("channel_id")] + public ulong ChannelId { get; set; } + [JsonProperty("message_id")] + public ulong MessageId { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 4454e619f..a150a6d15 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -83,6 +83,12 @@ namespace Discord.WebSocket remove { _reactionRemovedEvent.Remove(value); } } private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, Task> ReactionsCleared + { + add { _reactionsClearedEvent.Add(value); } + remove { _reactionsClearedEvent.Remove(value); } + } + private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 238cdbcb1..ebe7c6835 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1306,7 +1306,7 @@ namespace Discord.WebSocket break; case "MESSAGE_REACTION_ADD": { - await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; @@ -1333,7 +1333,7 @@ namespace Discord.WebSocket } case "MESSAGE_REACTION_REMOVE": { - await _gatewayLogger.DebugAsync("Received Disbatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; @@ -1355,6 +1355,31 @@ namespace Discord.WebSocket await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); return; } + break; + } + case "MESSAGE_REACTION_REMOVE_ALL": + { + await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE_ALL)").ConfigureAwait(false); + + var data = (payload as JToken).ToObject(_serializer); + var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; + if (channel != null) + { + SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + if (cachedMsg != null) + { + cachedMsg.ClearReactions(); + await _reactionsClearedEvent.InvokeAsync(data.MessageId, cachedMsg).ConfigureAwait(false); + return; + } + await _reactionsClearedEvent.InvokeAsync(data.MessageId, Optional.Create()); + } + else + { + await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false); + return; + } + break; } case "MESSAGE_DELETE_BULK": diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 43e61f670..cd65f4513 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -122,6 +122,10 @@ namespace Discord.WebSocket if (_reactions.Contains(reaction)) _reactions.Remove(reaction); } + internal void ClearReactions() + { + _reactions.Clear(); + } public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); From fd5e237b41427ba530bfbddd6b422deb183dde66 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sun, 20 Nov 2016 13:40:40 +0000 Subject: [PATCH 011/263] Fix commands from being created with invalid aliases --- .../Builders/ModuleClassBuilder.cs | 8 +++++++- src/Discord.Net.Commands/Info/CommandInfo.cs | 14 +++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 9884f18db..31694d1d1 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -116,6 +116,9 @@ namespace Discord.Commands { var attributes = method.GetCustomAttributes(); + builder.Name = method.Name; + + var setName = false; foreach (var attribute in attributes) { // TODO: C#7 type switch @@ -124,10 +127,13 @@ namespace Discord.Commands var cmdAttr = attribute as CommandAttribute; builder.AddAliases(cmdAttr.Text); builder.RunMode = cmdAttr.RunMode; - builder.Name = builder.Name ?? cmdAttr.Text; + builder.Name = setName ? builder.Name ?? cmdAttr.Text : cmdAttr.Text ?? builder.Name; } else if (attribute is NameAttribute) + { builder.Name = (attribute as NameAttribute).Text; + setName = true; + } else if (attribute is PriorityAttribute) builder.Priority = (attribute as PriorityAttribute).Priority; else if (attribute is SummaryAttribute) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 4d546b6fa..06b0f25a3 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -39,11 +39,19 @@ namespace Discord.Commands RunMode = builder.RunMode; Priority = builder.Priority; - - if (module.Aliases.Count != 0) + + // both command and module provide aliases + if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => first + " " + second).ToImmutableArray(); - else + // only module provides aliases + else if (module.Aliases.Count > 0) + Aliases = module.Aliases.ToImmutableArray(); + // only command provides aliases + else if (builder.Aliases.Count > 0) Aliases = builder.Aliases.ToImmutableArray(); + // neither provide aliases + else + throw new InvalidOperationException("Cannot build a command without any aliases"); Preconditions = builder.Preconditions.ToImmutableArray(); From 05fb81c6176eb361e66a6db9946873fc26401417 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sun, 20 Nov 2016 20:20:23 +0000 Subject: [PATCH 012/263] Fix a bunch of issues with aliases --- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 7 ++++--- src/Discord.Net.Commands/CommandService.cs | 4 ++-- src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 31694d1d1..57b40b6b3 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -118,7 +118,6 @@ namespace Discord.Commands builder.Name = method.Name; - var setName = false; foreach (var attribute in attributes) { // TODO: C#7 type switch @@ -127,12 +126,11 @@ namespace Discord.Commands var cmdAttr = attribute as CommandAttribute; builder.AddAliases(cmdAttr.Text); builder.RunMode = cmdAttr.RunMode; - builder.Name = setName ? builder.Name ?? cmdAttr.Text : cmdAttr.Text ?? builder.Name; + builder.Name = builder.Name ?? cmdAttr.Text; } else if (attribute is NameAttribute) { builder.Name = (attribute as NameAttribute).Text; - setName = true; } else if (attribute is PriorityAttribute) builder.Priority = (attribute as PriorityAttribute).Priority; @@ -146,6 +144,9 @@ namespace Discord.Commands builder.AddPrecondition(attribute as PreconditionAttribute); } + if (builder.Name == null) + builder.Name = method.Name; + var parameters = method.GetParameters(); int pos = 0, count = parameters.Length; foreach (var paramInfo in parameters) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 3c3760908..993dc7ebd 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -19,8 +19,8 @@ namespace Discord.Commands private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; - public IEnumerable Modules => _typedModuleDefs.Select(x => x.Value); - public IEnumerable Commands => _typedModuleDefs.SelectMany(x => x.Value.Commands); + public IEnumerable Modules => _moduleDefs.Select(x => x); + public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); public CommandService() { diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 06b0f25a3..55fdf0d90 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -42,7 +42,7 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => first + " " + second).ToImmutableArray(); + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).ToImmutableArray(); // only module provides aliases else if (module.Aliases.Count > 0) Aliases = module.Aliases.ToImmutableArray(); From 09608b79c341cbae088ed8400d50ba96eef9261b Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Tue, 22 Nov 2016 22:22:16 +0100 Subject: [PATCH 013/263] Adds builders for Thumbnail and Image embeds, and adds json model for Video and Image embeds. --- src/Discord.Net.Core/API/Common/Embed.cs | 4 + src/Discord.Net.Core/API/Common/EmbedImage.cs | 17 ++++ src/Discord.Net.Core/API/Common/EmbedVideo.cs | 15 +++ .../Entities/Messages/EmbedBuilder.cs | 97 ++++++++++++++++++- .../Entities/Messages/EmbedImage.cs | 31 ++++++ .../Entities/Messages/EmbedVideo.cs | 29 ++++++ .../Entities/Messages/IEmbed.cs | 2 + .../Entities/Messages/Embed.cs | 8 ++ 8 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 src/Discord.Net.Core/API/Common/EmbedImage.cs create mode 100644 src/Discord.Net.Core/API/Common/EmbedVideo.cs create mode 100644 src/Discord.Net.Core/Entities/Messages/EmbedImage.cs create mode 100644 src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs diff --git a/src/Discord.Net.Core/API/Common/Embed.cs b/src/Discord.Net.Core/API/Common/Embed.cs index ed4715237..eb56ce696 100644 --- a/src/Discord.Net.Core/API/Common/Embed.cs +++ b/src/Discord.Net.Core/API/Common/Embed.cs @@ -19,8 +19,12 @@ namespace Discord.API public Optional Author { get; set; } [JsonProperty("footer")] public Optional Footer { get; set; } + [JsonProperty("video")] + public Optional Video { get; set; } [JsonProperty("thumbnail")] public Optional Thumbnail { get; set; } + [JsonProperty("image")] + public Optional Image { get; set; } [JsonProperty("provider")] public Optional Provider { get; set; } [JsonProperty("fields")] diff --git a/src/Discord.Net.Core/API/Common/EmbedImage.cs b/src/Discord.Net.Core/API/Common/EmbedImage.cs new file mode 100644 index 000000000..ab4941ae0 --- /dev/null +++ b/src/Discord.Net.Core/API/Common/EmbedImage.cs @@ -0,0 +1,17 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API +{ + public class EmbedImage + { + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("proxy_url")] + public string ProxyUrl { get; set; } + [JsonProperty("height")] + public Optional Height { get; set; } + [JsonProperty("width")] + public Optional Width { get; set; } + } +} diff --git a/src/Discord.Net.Core/API/Common/EmbedVideo.cs b/src/Discord.Net.Core/API/Common/EmbedVideo.cs new file mode 100644 index 000000000..072004631 --- /dev/null +++ b/src/Discord.Net.Core/API/Common/EmbedVideo.cs @@ -0,0 +1,15 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API +{ + public class EmbedVideo + { + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("height")] + public Optional Height { get; set; } + [JsonProperty("width")] + public Optional Width { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 5c0c72e8f..08c860d00 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -4,6 +4,8 @@ using Embed = Discord.API.Embed; using Field = Discord.API.EmbedField; using Author = Discord.API.EmbedAuthor; using Footer = Discord.API.EmbedFooter; +using Thumbnail = Discord.API.EmbedThumbnail; +using Image = Discord.API.EmbedImage; namespace Discord { @@ -14,8 +16,7 @@ namespace Discord public EmbedBuilder() { - _model = new Embed(); - _model.Type = "rich"; + _model = new Embed {Type = "rich"}; _fields = new List(); } @@ -25,6 +26,8 @@ namespace Discord public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } public EmbedAuthorBuilder Author { get; set; } public EmbedFooterBuilder Footer { get; set; } + public EmbedThumbnailBuilder Thumbnail { get; set; } + public EmbedImageBuilder Image { get; set; } public EmbedBuilder WithTitle(string title) { @@ -71,6 +74,30 @@ namespace Discord Footer = footer; return this; } + public EmbedBuilder WithThumbnail(EmbedThumbnailBuilder thumbnail) + { + Thumbnail = thumbnail; + return this; + } + public EmbedBuilder WithThumbnail(Action action) + { + var thumbnail = new EmbedThumbnailBuilder(); + action(thumbnail); + Thumbnail = thumbnail; + return this; + } + public EmbedBuilder WithImage(EmbedImageBuilder image) + { + Image = image; + return this; + } + public EmbedBuilder WithImage(Action action) + { + var image = new EmbedImageBuilder(); + action(image); + Image = image; + return this; + } public EmbedBuilder AddField(Action action) { @@ -84,6 +111,8 @@ namespace Discord { _model.Author = Author?.ToModel(); _model.Footer = Footer?.ToModel(); + _model.Thumbnail = Thumbnail?.ToModel(); + _model.Image = Image?.ToModel(); _model.Fields = _fields.ToArray(); return _model; } @@ -178,4 +207,68 @@ namespace Discord internal Footer ToModel() => _model; } + + public class EmbedThumbnailBuilder + { + private Thumbnail _model; + + public string Url { get { return _model.Url; } set { _model.Url = value; } } + public Optional Height { get { return _model.Height; } set { _model.Height = value; } } + public Optional Width { get { return _model.Width; } set { _model.Width = value; } } + + public EmbedThumbnailBuilder() + { + _model = new Thumbnail(); + } + + public EmbedThumbnailBuilder WithUrl(string url) + { + Url = url; + return this; + } + public EmbedThumbnailBuilder WithHeight(int height) + { + Height = height; + return this; + } + public EmbedThumbnailBuilder WithWidth(int width) + { + Width = width; + return this; + } + + internal Thumbnail ToModel() => _model; + } + + public class EmbedImageBuilder + { + private Image _model; + + public string Url { get { return _model.Url; } set { _model.Url = value; } } + public Optional Height { get { return _model.Height; } set { _model.Height = value; } } + public Optional Width { get { return _model.Width; } set { _model.Width = value; } } + + public EmbedImageBuilder() + { + _model = new Image(); + } + + public EmbedImageBuilder WithUrl(string url) + { + Url = url; + return this; + } + public EmbedImageBuilder WithHeight(int height) + { + Height = height; + return this; + } + public EmbedImageBuilder WithWidth(int width) + { + Width = width; + return this; + } + + internal Image ToModel() => _model; + } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs new file mode 100644 index 000000000..763d21c1f --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs @@ -0,0 +1,31 @@ +using System.Diagnostics; +using Model = Discord.API.EmbedImage; + +namespace Discord +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public struct EmbedImage + { + public string Url { get; } + public string ProxyUrl { get; } + public int? Height { get; } + public int? Width { get; } + + private EmbedImage(string url, string proxyUrl, int? height, int? width) + { + Url = url; + ProxyUrl = proxyUrl; + Height = height; + Width = width; + } + internal static EmbedImage Create(Model model) + { + return new EmbedImage(model.Url, model.ProxyUrl, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + + private string DebuggerDisplay => $"{ToString()} ({Url})"; + public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs new file mode 100644 index 000000000..6d772252c --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using Model = Discord.API.EmbedVideo; + +namespace Discord +{ + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public struct EmbedVideo + { + public string Url { get; } + public int? Height { get; } + public int? Width { get; } + + private EmbedVideo(string url, int? height, int? width) + { + Url = url; + Height = height; + Width = width; + } + internal static EmbedVideo Create(Model model) + { + return new EmbedVideo(model.Url, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + + private string DebuggerDisplay => $"{ToString()} ({Url})"; + public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs index c46b22d17..86463534d 100644 --- a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs +++ b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs @@ -9,6 +9,8 @@ namespace Discord string Title { get; } string Description { get; } Color? Color { get; } + EmbedImage? Image { get; } + EmbedVideo? Video { get; } EmbedAuthor? Author { get; } EmbedFooter? Footer { get; } EmbedProvider? Provider { get; } diff --git a/src/Discord.Net.Rest/Entities/Messages/Embed.cs b/src/Discord.Net.Rest/Entities/Messages/Embed.cs index 540a39ea2..7124df417 100644 --- a/src/Discord.Net.Rest/Entities/Messages/Embed.cs +++ b/src/Discord.Net.Rest/Entities/Messages/Embed.cs @@ -14,6 +14,8 @@ namespace Discord public string Title { get; } public string Type { get; } public Color? Color { get; } + public EmbedImage? Image { get; } + public EmbedVideo? Video { get; } public EmbedAuthor? Author { get; } public EmbedFooter? Footer { get; } public EmbedProvider? Provider { get; } @@ -25,6 +27,8 @@ namespace Discord string description, string url, Color? color, + EmbedImage? image, + EmbedVideo? video, EmbedAuthor? author, EmbedFooter? footer, EmbedProvider? provider, @@ -36,6 +40,8 @@ namespace Discord Description = description; Url = url; Color = color; + Image = image; + Video = video; Author = author; Footer = footer; Provider = provider; @@ -46,6 +52,8 @@ namespace Discord { return new Embed(model.Type, model.Title, model.Description, model.Url, model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, + model.Image.IsSpecified ? EmbedImage.Create(model.Image.Value) : (EmbedImage?)null, + model.Video.IsSpecified ? EmbedVideo.Create(model.Video.Value) : (EmbedVideo?)null, model.Author.IsSpecified ? EmbedAuthor.Create(model.Author.Value) : (EmbedAuthor?)null, model.Footer.IsSpecified ? EmbedFooter.Create(model.Footer.Value) : (EmbedFooter?)null, model.Provider.IsSpecified ? EmbedProvider.Create(model.Provider.Value) : (EmbedProvider?)null, From 6cdf5ee90ec5550f7a6b313d165a18ae4719c8f3 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Tue, 22 Nov 2016 23:38:43 +0100 Subject: [PATCH 014/263] Removed Height/Width. --- .../Entities/Messages/EmbedBuilder.cs | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 08c860d00..c0069aa9a 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -16,7 +16,7 @@ namespace Discord public EmbedBuilder() { - _model = new Embed {Type = "rich"}; + _model = new Embed { Type = "rich" }; _fields = new List(); } @@ -213,8 +213,6 @@ namespace Discord private Thumbnail _model; public string Url { get { return _model.Url; } set { _model.Url = value; } } - public Optional Height { get { return _model.Height; } set { _model.Height = value; } } - public Optional Width { get { return _model.Width; } set { _model.Width = value; } } public EmbedThumbnailBuilder() { @@ -226,16 +224,6 @@ namespace Discord Url = url; return this; } - public EmbedThumbnailBuilder WithHeight(int height) - { - Height = height; - return this; - } - public EmbedThumbnailBuilder WithWidth(int width) - { - Width = width; - return this; - } internal Thumbnail ToModel() => _model; } @@ -245,8 +233,6 @@ namespace Discord private Image _model; public string Url { get { return _model.Url; } set { _model.Url = value; } } - public Optional Height { get { return _model.Height; } set { _model.Height = value; } } - public Optional Width { get { return _model.Width; } set { _model.Width = value; } } public EmbedImageBuilder() { @@ -258,16 +244,6 @@ namespace Discord Url = url; return this; } - public EmbedImageBuilder WithHeight(int height) - { - Height = height; - return this; - } - public EmbedImageBuilder WithWidth(int width) - { - Width = width; - return this; - } internal Image ToModel() => _model; } From f8e3ac9910656d68096e31dcc3ef2a94c6bffc51 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Wed, 23 Nov 2016 00:10:01 +0100 Subject: [PATCH 015/263] Something like this perhaps --- src/Discord.Net.Core/Entities/Messages/EmbedImage.cs | 4 ++-- src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs | 4 ++-- src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs index 763d21c1f..47513dc19 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"{ToString()} ({Url})"; - public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; + private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs index 6a5fc4163..8acd2c134 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"{ToString()} ({Url})"; - public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; + private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs index 6d772252c..8289d4813 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs @@ -23,7 +23,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"{ToString()} ({Url})"; - public override string ToString() => Width != null && Height != null ? $"{Width}x{Height}" : "0x0"; + private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + public override string ToString() => Url; } } From 4cb79297356c1f2fea0298718df178de42b822b0 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Wed, 23 Nov 2016 00:27:33 +0100 Subject: [PATCH 016/263] Spacing --- src/Discord.Net.Core/Entities/Messages/EmbedImage.cs | 2 +- src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs | 2 +- src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs index 47513dc19..76da598f1 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs index 8acd2c134..fe836d0f6 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs index 8289d4813..18ee4ccfa 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs @@ -23,7 +23,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}){(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; public override string ToString() => Url; } } From 8cf0911a4be20502fe37e3dc071b3591c8ff84e4 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Wed, 23 Nov 2016 00:35:31 +0100 Subject: [PATCH 017/263] DebuggerDisplay fix. --- src/Discord.Net.Core/Entities/Messages/EmbedImage.cs | 2 +- src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs | 2 +- src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs index 76da598f1..0b606c11e 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs index fe836d0f6..dac2f5c7c 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs @@ -25,7 +25,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; } } diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs index 18ee4ccfa..3c93416d0 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs @@ -23,7 +23,7 @@ namespace Discord model.Width.IsSpecified ? model.Width.Value : (int?)null); } - private string DebuggerDisplay => $"({Url}) {(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")}"; + private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; } } From a455ccc3343e058fcb0c6e3714128b33d1da4e49 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Thu, 24 Nov 2016 01:17:39 +0100 Subject: [PATCH 018/263] Adds a timestamp and fixes Volts issues with Thumbnail and Image URLs. --- src/Discord.Net.Core/API/Common/Embed.cs | 3 + .../Entities/Messages/EmbedBuilder.cs | 82 ++++--------------- .../Entities/Messages/IEmbed.cs | 4 +- .../Entities/Messages/Embed.cs | 8 +- 4 files changed, 26 insertions(+), 71 deletions(-) diff --git a/src/Discord.Net.Core/API/Common/Embed.cs b/src/Discord.Net.Core/API/Common/Embed.cs index eb56ce696..5a6661f0a 100644 --- a/src/Discord.Net.Core/API/Common/Embed.cs +++ b/src/Discord.Net.Core/API/Common/Embed.cs @@ -1,4 +1,5 @@ #pragma warning disable CS1591 +using System; using Newtonsoft.Json; namespace Discord.API @@ -15,6 +16,8 @@ namespace Discord.API public string Url { get; set; } [JsonProperty("color")] public uint? Color { get; set; } + [JsonProperty("timestamp")] + public Optional Timestamp { get; set; } [JsonProperty("author")] public Optional Author { get; set; } [JsonProperty("footer")] diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index c0069aa9a..f98371a31 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -23,11 +23,11 @@ namespace Discord public string Title { get { return _model.Title; } set { _model.Title = value; } } public string Description { get { return _model.Description; } set { _model.Description = value; } } public string Url { get { return _model.Url; } set { _model.Url = value; } } + public string ThumbnailUrl { get; set; } + public string ImageUrl { get; set; } public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } public EmbedAuthorBuilder Author { get; set; } public EmbedFooterBuilder Footer { get; set; } - public EmbedThumbnailBuilder Thumbnail { get; set; } - public EmbedImageBuilder Image { get; set; } public EmbedBuilder WithTitle(string title) { @@ -44,6 +44,16 @@ namespace Discord Url = url; return this; } + public EmbedBuilder WithThumbnailUrl(string thumbnailUrl) + { + ThumbnailUrl = thumbnailUrl; + return this; + } + public EmbedBuilder WithImageUrl(string imageUrl) + { + ImageUrl = ImageUrl; + return this; + } public EmbedBuilder WithColor(Color color) { Color = color; @@ -74,30 +84,6 @@ namespace Discord Footer = footer; return this; } - public EmbedBuilder WithThumbnail(EmbedThumbnailBuilder thumbnail) - { - Thumbnail = thumbnail; - return this; - } - public EmbedBuilder WithThumbnail(Action action) - { - var thumbnail = new EmbedThumbnailBuilder(); - action(thumbnail); - Thumbnail = thumbnail; - return this; - } - public EmbedBuilder WithImage(EmbedImageBuilder image) - { - Image = image; - return this; - } - public EmbedBuilder WithImage(Action action) - { - var image = new EmbedImageBuilder(); - action(image); - Image = image; - return this; - } public EmbedBuilder AddField(Action action) { @@ -111,8 +97,8 @@ namespace Discord { _model.Author = Author?.ToModel(); _model.Footer = Footer?.ToModel(); - _model.Thumbnail = Thumbnail?.ToModel(); - _model.Image = Image?.ToModel(); + _model.Thumbnail = ThumbnailUrl != null ? new Thumbnail { Url = ThumbnailUrl } : null; + _model.Image = ImageUrl != null ? new Image { Url = ImageUrl } : null; _model.Fields = _fields.ToArray(); return _model; } @@ -207,44 +193,4 @@ namespace Discord internal Footer ToModel() => _model; } - - public class EmbedThumbnailBuilder - { - private Thumbnail _model; - - public string Url { get { return _model.Url; } set { _model.Url = value; } } - - public EmbedThumbnailBuilder() - { - _model = new Thumbnail(); - } - - public EmbedThumbnailBuilder WithUrl(string url) - { - Url = url; - return this; - } - - internal Thumbnail ToModel() => _model; - } - - public class EmbedImageBuilder - { - private Image _model; - - public string Url { get { return _model.Url; } set { _model.Url = value; } } - - public EmbedImageBuilder() - { - _model = new Image(); - } - - public EmbedImageBuilder WithUrl(string url) - { - Url = url; - return this; - } - - internal Image ToModel() => _model; - } } diff --git a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs index 86463534d..d684827c3 100644 --- a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs +++ b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System; +using System.Collections.Immutable; namespace Discord { @@ -9,6 +10,7 @@ namespace Discord string Title { get; } string Description { get; } Color? Color { get; } + DateTimeOffset? Timestamp { get; } EmbedImage? Image { get; } EmbedVideo? Video { get; } EmbedAuthor? Author { get; } diff --git a/src/Discord.Net.Rest/Entities/Messages/Embed.cs b/src/Discord.Net.Rest/Entities/Messages/Embed.cs index 7124df417..f51e7e7a7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/Embed.cs +++ b/src/Discord.Net.Rest/Entities/Messages/Embed.cs @@ -14,6 +14,7 @@ namespace Discord public string Title { get; } public string Type { get; } public Color? Color { get; } + public DateTimeOffset? Timestamp { get; } public EmbedImage? Image { get; } public EmbedVideo? Video { get; } public EmbedAuthor? Author { get; } @@ -26,7 +27,8 @@ namespace Discord string title, string description, string url, - Color? color, + Color? color, + DateTimeOffset? timestamp, EmbedImage? image, EmbedVideo? video, EmbedAuthor? author, @@ -40,6 +42,7 @@ namespace Discord Description = description; Url = url; Color = color; + Timestamp = timestamp; Image = image; Video = video; Author = author; @@ -52,13 +55,14 @@ namespace Discord { return new Embed(model.Type, model.Title, model.Description, model.Url, model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, + model.Timestamp.IsSpecified ? model.Timestamp.Value : (DateTimeOffset?)null, model.Image.IsSpecified ? EmbedImage.Create(model.Image.Value) : (EmbedImage?)null, model.Video.IsSpecified ? EmbedVideo.Create(model.Video.Value) : (EmbedVideo?)null, model.Author.IsSpecified ? EmbedAuthor.Create(model.Author.Value) : (EmbedAuthor?)null, model.Footer.IsSpecified ? EmbedFooter.Create(model.Footer.Value) : (EmbedFooter?)null, model.Provider.IsSpecified ? EmbedProvider.Create(model.Provider.Value) : (EmbedProvider?)null, model.Thumbnail.IsSpecified ? EmbedThumbnail.Create(model.Thumbnail.Value) : (EmbedThumbnail?)null, - model.Fields.IsSpecified ? model.Fields.Value.Select(x => EmbedField.Create(x)).ToImmutableArray() : ImmutableArray.Create()); + model.Fields.IsSpecified ? model.Fields.Value.Select(EmbedField.Create).ToImmutableArray() : ImmutableArray.Create()); } public override string ToString() => Title; From d0158816d373261f72458b63c2d415757bf2fa47 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Fri, 25 Nov 2016 17:09:17 +0100 Subject: [PATCH 019/263] Adds DateFormatString to DiscordRestApiClient --- src/Discord.Net.Core/API/Common/Embed.cs | 2 +- .../API/DiscordRestApiClient.cs | 2 +- .../Entities/Messages/EmbedBuilder.cs | 20 +++++++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Core/API/Common/Embed.cs b/src/Discord.Net.Core/API/Common/Embed.cs index 5a6661f0a..33b97e641 100644 --- a/src/Discord.Net.Core/API/Common/Embed.cs +++ b/src/Discord.Net.Core/API/Common/Embed.cs @@ -17,7 +17,7 @@ namespace Discord.API [JsonProperty("color")] public uint? Color { get; set; } [JsonProperty("timestamp")] - public Optional Timestamp { get; set; } + public DateTimeOffset? Timestamp { get; set; } [JsonProperty("author")] public Optional Author { get; set; } [JsonProperty("footer")] diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index d050f12a4..a03351a7b 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -49,7 +49,7 @@ namespace Discord.API { _restClientProvider = restClientProvider; _userAgent = userAgent; - _serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() }; + _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", DateTimeZoneHandling = DateTimeZoneHandling.Utc, ContractResolver = new DiscordContractResolver() }; RequestQueue = requestQueue; FetchCurrentUser = true; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index f98371a31..045af3b05 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -20,12 +20,13 @@ namespace Discord _fields = new List(); } - public string Title { get { return _model.Title; } set { _model.Title = value; } } + public string Title { get { return _model.Title; } set { _model.Title = value; } } public string Description { get { return _model.Description; } set { _model.Description = value; } } public string Url { get { return _model.Url; } set { _model.Url = value; } } public string ThumbnailUrl { get; set; } public string ImageUrl { get; set; } public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } + public DateTimeOffset? Timestamp { get; set; } public EmbedAuthorBuilder Author { get; set; } public EmbedFooterBuilder Footer { get; set; } @@ -59,6 +60,16 @@ namespace Discord Color = color; return this; } + public EmbedBuilder WithTimestamp() + { + Timestamp = DateTimeOffset.UtcNow; + return this; + } + public EmbedBuilder WithTimestamp(DateTimeOffset dateTimeOffset) + { + Timestamp = dateTimeOffset; + return this; + } public EmbedBuilder WithAuthor(EmbedAuthorBuilder author) { @@ -97,6 +108,7 @@ namespace Discord { _model.Author = Author?.ToModel(); _model.Footer = Footer?.ToModel(); + _model.Timestamp = Timestamp?.ToUniversalTime(); _model.Thumbnail = ThumbnailUrl != null ? new Thumbnail { Url = ThumbnailUrl } : null; _model.Image = ImageUrl != null ? new Image { Url = ImageUrl } : null; _model.Fields = _fields.ToArray(); @@ -106,7 +118,7 @@ namespace Discord public class EmbedFieldBuilder { - private Field _model; + private readonly Field _model; public string Name { get { return _model.Name; } set { _model.Name = value; } } public string Value { get { return _model.Value; } set { _model.Value = value; } } @@ -138,7 +150,7 @@ namespace Discord public class EmbedAuthorBuilder { - private Author _model; + private readonly Author _model; public string Name { get { return _model.Name; } set { _model.Name = value; } } public string Url { get { return _model.Url; } set { _model.Url = value; } } @@ -170,7 +182,7 @@ namespace Discord public class EmbedFooterBuilder { - private Footer _model; + private readonly Footer _model; public string Text { get { return _model.Text; } set { _model.Text = value; } } public string IconUrl { get { return _model.IconUrl; } set { _model.IconUrl = value; } } From 54dd0a5cec3d67d7fed120d7bb07d532d3be6036 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Fri, 25 Nov 2016 21:51:01 +0100 Subject: [PATCH 020/263] Fixed ordering. --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 2 +- .../Entities/Messages/EmbedBuilder.cs | 12 ++++++------ src/Discord.Net.Core/Entities/Messages/IEmbed.cs | 2 +- src/Discord.Net.Rest/Entities/Messages/Embed.cs | 15 +++++++-------- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index a03351a7b..451470d73 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -49,7 +49,7 @@ namespace Discord.API { _restClientProvider = restClientProvider; _userAgent = userAgent; - _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", DateTimeZoneHandling = DateTimeZoneHandling.Utc, ContractResolver = new DiscordContractResolver() }; + _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; RequestQueue = requestQueue; FetchCurrentUser = true; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 045af3b05..aac361888 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -25,8 +25,8 @@ namespace Discord public string Url { get { return _model.Url; } set { _model.Url = value; } } public string ThumbnailUrl { get; set; } public string ImageUrl { get; set; } - public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } public DateTimeOffset? Timestamp { get; set; } + public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } public EmbedAuthorBuilder Author { get; set; } public EmbedFooterBuilder Footer { get; set; } @@ -55,11 +55,6 @@ namespace Discord ImageUrl = ImageUrl; return this; } - public EmbedBuilder WithColor(Color color) - { - Color = color; - return this; - } public EmbedBuilder WithTimestamp() { Timestamp = DateTimeOffset.UtcNow; @@ -70,6 +65,11 @@ namespace Discord Timestamp = dateTimeOffset; return this; } + public EmbedBuilder WithColor(Color color) + { + Color = color; + return this; + } public EmbedBuilder WithAuthor(EmbedAuthorBuilder author) { diff --git a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs index d684827c3..5eef5ec9b 100644 --- a/src/Discord.Net.Core/Entities/Messages/IEmbed.cs +++ b/src/Discord.Net.Core/Entities/Messages/IEmbed.cs @@ -9,8 +9,8 @@ namespace Discord string Type { get; } string Title { get; } string Description { get; } - Color? Color { get; } DateTimeOffset? Timestamp { get; } + Color? Color { get; } EmbedImage? Image { get; } EmbedVideo? Video { get; } EmbedAuthor? Author { get; } diff --git a/src/Discord.Net.Rest/Entities/Messages/Embed.cs b/src/Discord.Net.Rest/Entities/Messages/Embed.cs index f51e7e7a7..a291dc6c0 100644 --- a/src/Discord.Net.Rest/Entities/Messages/Embed.cs +++ b/src/Discord.Net.Rest/Entities/Messages/Embed.cs @@ -13,8 +13,8 @@ namespace Discord public string Url { get; } public string Title { get; } public string Type { get; } - public Color? Color { get; } public DateTimeOffset? Timestamp { get; } + public Color? Color { get; } public EmbedImage? Image { get; } public EmbedVideo? Video { get; } public EmbedAuthor? Author { get; } @@ -24,11 +24,11 @@ namespace Discord public ImmutableArray Fields { get; } internal Embed(string type, - string title, - string description, - string url, - Color? color, + string title, + string description, + string url, DateTimeOffset? timestamp, + Color? color, EmbedImage? image, EmbedVideo? video, EmbedAuthor? author, @@ -53,9 +53,8 @@ namespace Discord } internal static Embed Create(Model model) { - return new Embed(model.Type, model.Title, model.Description, model.Url, - model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, - model.Timestamp.IsSpecified ? model.Timestamp.Value : (DateTimeOffset?)null, + return new Embed(model.Type, model.Title, model.Description, model.Url,model.Timestamp, + model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, model.Image.IsSpecified ? EmbedImage.Create(model.Image.Value) : (EmbedImage?)null, model.Video.IsSpecified ? EmbedVideo.Create(model.Video.Value) : (EmbedVideo?)null, model.Author.IsSpecified ? EmbedAuthor.Create(model.Author.Value) : (EmbedAuthor?)null, From bc76e38ce54a1d273f52e04c3ce78fe89e5a28bd Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Nov 2016 21:15:39 +0000 Subject: [PATCH 021/263] Fix additional issue with aliases building incorrectly --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 64fa29ea2..3baa2d34f 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -49,15 +49,21 @@ namespace Discord.Commands while (builderStack.Count() > 0) { - ModuleBuilder level = builderStack.Pop(); // get the topmost builder + ModuleBuilder level = builderStack.Pop(); //get the topmost builder if (result == null) - result = level.Aliases.ToList(); // create a shallow copy so we don't overwrite the builder unexpectedly + { + if (level.Aliases.Count > 0) + result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly + } else if (result.Count() > level.Aliases.Count) result = result.Permutate(level.Aliases, (first, second) => first + " " + second); else result = level.Aliases.Permutate(result, (second, first) => first + " " + second); } + if (result == null) //there were no aliases; default to an empty list + result = new List(); + return result; } From 5cdda592c19a6855ef761420235cd200f0164051 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Nov 2016 21:19:41 +0000 Subject: [PATCH 022/263] Add requested changes --- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 57b40b6b3..aaec43161 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -115,9 +115,7 @@ namespace Discord.Commands private static void BuildCommand(CommandBuilder builder, TypeInfo typeInfo, MethodInfo method, CommandService service) { var attributes = method.GetCustomAttributes(); - - builder.Name = method.Name; - + foreach (var attribute in attributes) { // TODO: C#7 type switch @@ -129,9 +127,7 @@ namespace Discord.Commands builder.Name = builder.Name ?? cmdAttr.Text; } else if (attribute is NameAttribute) - { builder.Name = (attribute as NameAttribute).Text; - } else if (attribute is PriorityAttribute) builder.Priority = (attribute as PriorityAttribute).Priority; else if (attribute is SummaryAttribute) From fdecfe6bd4adcd0fc1d01948cf996ac0c8039eac Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 17:44:37 -0500 Subject: [PATCH 023/263] Add RequireOwner Precondition This precondition will require that the invoker of the command is the owner of the bot. --- .../Preconditions/RequireOwnerAttribute.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs new file mode 100644 index 000000000..e03e3971b --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Discord; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class RequireOwnerAttribute : PreconditionAttribute + { + private IApplication application; + + public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + { + if (application == null) + application = await context.Client.GetApplicationInfoAsync(); + if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess(); + return PreconditionResult.FromError("Command can only be run by the owner of the bot"); + } + } +} From ea148db08b2c2fa643167a4d0703113ee04a44ab Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 17:48:08 -0500 Subject: [PATCH 024/263] Add RequireBotPermission, rename RequirePermission This is a breaking change. Adds a precondition that requires the bot has a specified permission (Resolves #211). Renames RequirePermission to RequireUserPermission. --- .../RequireBotPermissionAttribute.cs | 54 +++++++++++++++++++ ...e.cs => RequireUserPermissionAttribute.cs} | 6 +-- 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs rename src/Discord.Net.Commands/Attributes/Preconditions/{RequirePermissionAttribute.cs => RequireUserPermissionAttribute.cs} (89%) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs new file mode 100644 index 000000000..d3c598591 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class RequireBotPermissionAttribute : PreconditionAttribute + { + public GuildPermission? GuildPermission { get; } + public ChannelPermission? ChannelPermission { get; } + + public RequireBotPermissionAttribute(GuildPermission permission) + { + GuildPermission = permission; + ChannelPermission = null; + } + public RequireBotPermissionAttribute(ChannelPermission permission) + { + ChannelPermission = permission; + GuildPermission = null; + } + + public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + { + var guildUser = await context.Guild.GetCurrentUserAsync(); + + if (GuildPermission.HasValue) + { + if (guildUser == null) + return PreconditionResult.FromError("Command must be used in a guild channel"); + if (!guildUser.GuildPermissions.Has(GuildPermission.Value)) + return PreconditionResult.FromError($"Command requires guild permission {GuildPermission.Value}"); + } + + if (ChannelPermission.HasValue) + { + var guildChannel = context.Channel as IGuildChannel; + + ChannelPermissions perms; + if (guildChannel != null) + perms = guildUser.GetPermissions(guildChannel); + else + perms = ChannelPermissions.All(guildChannel); + + if (!perms.Has(ChannelPermission.Value)) + return PreconditionResult.FromError($"Command requires channel permission {ChannelPermission.Value}"); + } + + return PreconditionResult.FromSuccess(); + } + } +} diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs similarity index 89% rename from src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs rename to src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 26aeac5ec..2e19b61cf 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequirePermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -4,17 +4,17 @@ using System.Threading.Tasks; namespace Discord.Commands { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] - public class RequirePermissionAttribute : PreconditionAttribute + public class RequireUserPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } - public RequirePermissionAttribute(GuildPermission permission) + public RequireUserPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } - public RequirePermissionAttribute(ChannelPermission permission) + public RequireUserPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; GuildPermission = null; From defc8f1c4eeee8302ec30549a021abfa5649beb0 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 18:09:18 -0500 Subject: [PATCH 025/263] Add docstrings to preconditions. --- .../RequireBotPermissionAttribute.cs | 21 ++++++++++++++++++ .../Preconditions/RequireContextAttribute.cs | 16 ++++++++++++++ .../Preconditions/RequireOwnerAttribute.cs | 4 ++++ .../RequireUserPermissionAttribute.cs | 22 +++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs index d3c598591..914978192 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -5,17 +5,38 @@ using System.Threading.Tasks; namespace Discord.Commands { + /// + /// This attribute requires that the bot has a speicifed permission in the channel a command is invoked in. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireBotPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } + /// + /// Require that the bot account has a specified GuildPermission + /// + /// This precondition will always fail if the command is being invoked in a private channel. + /// The GuildPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. public RequireBotPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } + /// + /// Require that the bot account has a specified ChannelPermission. + /// + /// The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// + /// + /// [Command("permission")] + /// [RequireBotPermission(ChannelPermission.ManageMessages)] + /// public async Task Purge() + /// { + /// } + /// + /// public RequireBotPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index 1cd32e72e..beadbbc89 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -11,11 +11,27 @@ namespace Discord.Commands Group = 0x04 } + /// + /// Require that the command be invoked in a specified context. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireContextAttribute : PreconditionAttribute { public ContextType Contexts { get; } + /// + /// Require that the command be invoked in a specified context. + /// + /// The type of context the command can be invoked in. Multiple contexts can be speicifed by ORing the contexts together. + /// + /// + /// [Command("private_only")] + /// [RequireContext(ContextType.DM | ContextType.Group)] + /// public async Task PrivateOnly() + /// { + /// } + /// + /// public RequireContextAttribute(ContextType contexts) { Contexts = contexts; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index e03e3971b..beb8dfdd0 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -6,6 +6,10 @@ using Discord; namespace Discord.Commands { + /// + /// Require that the command is invoked by the owner of the bot. + /// + /// This precondition will only work if the bot is a bot account. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 2e19b61cf..17cf234aa 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -3,17 +3,39 @@ using System.Threading.Tasks; namespace Discord.Commands { + /// + /// This attribute requires that the user invoking the command has a specified permission. + /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class RequireUserPermissionAttribute : PreconditionAttribute { public GuildPermission? GuildPermission { get; } public ChannelPermission? ChannelPermission { get; } + /// + /// Require that the user invoking the command has a specified GuildPermission + /// + /// This precondition will always fail if the command is being invoked in a private channel. + /// The GuildPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. public RequireUserPermissionAttribute(GuildPermission permission) { GuildPermission = permission; ChannelPermission = null; } + /// + /// Require that the user invoking the command has a specified ChannelPermission. + /// + /// The ChannelPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// + /// + /// [Command("permission")] + /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)] + /// public async Task HasPermission() + /// { + /// await ReplyAsync("You can read messages and the message history!"); + /// } + /// + /// public RequireUserPermissionAttribute(ChannelPermission permission) { ChannelPermission = permission; From 8222eaff8654b927dfe44afe4c5237c1d0cf8f06 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 18:27:20 -0500 Subject: [PATCH 026/263] Add permissions for reactions --- .../Entities/Permissions/ChannelPermission.cs | 1 + .../Entities/Permissions/ChannelPermissions.cs | 4 +++- .../Entities/Permissions/GuildPermission.cs | 1 + .../Entities/Permissions/GuildPermissions.cs | 6 ++++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs index 5bedfbfae..7698390f1 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs @@ -11,6 +11,7 @@ //ManageGuild = 5, //Text + AddReactions = 6, ReadMessages = 10, SendMessages = 11, SendTTSMessages = 12, diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index bada89a32..06635feed 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -10,7 +10,7 @@ namespace Discord //TODO: C#7 Candidate for binary literals private static ChannelPermissions _allDM { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001011100110000000000", 2)); private static ChannelPermissions _allVoice { get; } = new ChannelPermissions(Convert.ToUInt64("00010011111100000000000000010001", 2)); - private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000001111111110000010001", 2)); + private static ChannelPermissions _allText { get; } = new ChannelPermissions(Convert.ToUInt64("00010000000001111111110001010001", 2)); private static ChannelPermissions _allGroup { get; } = new ChannelPermissions(Convert.ToUInt64("00000000000001111110110000000000", 2)); /// Gets a blank ChannelPermissions that grants no permissions. @@ -35,6 +35,8 @@ namespace Discord /// If True, a user may create, delete and modify this channel. public bool ManageChannel => Permissions.GetValue(RawValue, ChannelPermission.ManageChannel); + /// If true, a user may add reactions. + public bool AddReactions => Permissions.GetValue(RawValue, ChannelPermission.AddReactions); /// If True, a user may join channels. public bool ReadMessages => Permissions.GetValue(RawValue, ChannelPermission.ReadMessages); /// If True, a user may send messages. diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs index e74a4da49..3975c1b8b 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs @@ -11,6 +11,7 @@ ManageGuild = 5, //Text + AddReactions = 6, ReadMessages = 10, SendMessages = 11, SendTTSMessages = 12, diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 5941fde97..92fca96bd 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -11,7 +11,7 @@ namespace Discord public static readonly GuildPermissions None = new GuildPermissions(); /// Gets a GuildPermissions that grants all permissions. //TODO: C#7 Candidate for binary literals - public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("01111111111100111111110000111111", 2)); + public static readonly GuildPermissions All = new GuildPermissions(Convert.ToUInt64("01111111111100111111110001111111", 2)); /// Gets a packed value representing all the permissions in this GuildPermissions. public ulong RawValue { get; } @@ -28,7 +28,9 @@ namespace Discord public bool ManageChannels => Permissions.GetValue(RawValue, GuildPermission.ManageChannels); /// If True, a user may adjust guild properties. public bool ManageGuild => Permissions.GetValue(RawValue, GuildPermission.ManageGuild); - + + /// If true, a user may add reactions. + public bool AddReactions => Permissions.GetValue(RawValue, GuildPermission.AddReactions); /// If True, a user may join channels. public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); /// If True, a user may send messages. From f9c5e229d0fb5b66939d7703a3c109c70c7b3605 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 18:49:35 -0500 Subject: [PATCH 027/263] Add CommandServiceConfig, DefaultRunMode This adds an (optional) CommandServiceConfig, as well as a DefaultRunMode for commands. This resolves #368 (for commands where a RunMode is not explicitly specified, a custom default value should be used) --- src/Discord.Net.Commands/Attributes/CommandAttribute.cs | 2 +- src/Discord.Net.Commands/Builders/CommandBuilder.cs | 2 +- src/Discord.Net.Commands/CommandService.cs | 6 +++++- src/Discord.Net.Commands/CommandServiceConfig.cs | 8 ++++++++ src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 src/Discord.Net.Commands/CommandServiceConfig.cs diff --git a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs index baac75ff9..86daf0103 100644 --- a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs @@ -6,7 +6,7 @@ namespace Discord.Commands public class CommandAttribute : Attribute { public string Text { get; } - public RunMode RunMode { get; set; } = RunMode.Sync; + public RunMode RunMode { get; set; } public CommandAttribute() { diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index 9b983fd1f..25c0223e6 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -17,7 +17,7 @@ namespace Discord.Commands.Builders public string Name { get; set; } public string Summary { get; set; } public string Remarks { get; set; } - public RunMode RunMode { get; set; } + public RunMode? RunMode { get; set; } public int Priority { get; set; } public IReadOnlyList Preconditions => _preconditions; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 3c3760908..376eca84e 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -19,10 +19,13 @@ namespace Discord.Commands private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; + internal readonly RunMode _defaultRunMode; + public IEnumerable Modules => _typedModuleDefs.Select(x => x.Value); public IEnumerable Commands => _typedModuleDefs.SelectMany(x => x.Value.Commands); - public CommandService() + public CommandService() : this(new CommandServiceConfig()) { } + public CommandService(CommandServiceConfig config) { _moduleLock = new SemaphoreSlim(1, 1); _typedModuleDefs = new ConcurrentDictionary(); @@ -64,6 +67,7 @@ namespace Discord.Commands [typeof(IGroupUser)] = new UserTypeReader(), [typeof(IGuildUser)] = new UserTypeReader(), }; + _defaultRunMode = config.DefaultRunMode; } //Modules diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs new file mode 100644 index 000000000..97c98a54c --- /dev/null +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -0,0 +1,8 @@ +namespace Discord.Commands +{ + public class CommandServiceConfig + { + /// The default RunMode commands should have, if one is not specified on the Command attribute or builder. + public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; + } +} diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 4d546b6fa..a9a3c2415 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -37,7 +37,7 @@ namespace Discord.Commands Summary = builder.Summary; Remarks = builder.Remarks; - RunMode = builder.RunMode; + RunMode = builder.RunMode ?? service._defaultRunMode; Priority = builder.Priority; if (module.Aliases.Count != 0) From 58291f819968aee84e3c13114620f55f520577f4 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 21:45:56 -0500 Subject: [PATCH 028/263] Close the WebSocket when disconnecting This resolves #224 --- src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs b/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs index 707f7663b..ffa96dba7 100644 --- a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs +++ b/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs @@ -101,6 +101,8 @@ namespace Discord.Net.WebSockets if (_client != null && _client.State == WebSocketState.Open) { + var token = new CancellationToken(); + await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", token); _client.Dispose(); _client = null; } From 6519b300d9394b662d3306146e048fa24702d73f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 22:12:28 -0500 Subject: [PATCH 029/263] ModifyAsync will accept an EmbedBuilder **This is not a breaking change**. This change begins to implement #379, where IUserMessage.ModifyAsync will now accept a `Discord.ModifyMessageParams` func over a `Discord.API.Rest.ModifyMessageParams` func. --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 2 +- src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs | 2 ++ .../Entities/Messages/IUserMessage.cs | 3 +-- .../Entities/Messages/ModifyMessageParams.cs | 12 ++++++++++++ .../Entities/Messages/MessageHelper.cs | 7 ++++++- 5 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index d050f12a4..e280c730f 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -498,7 +498,7 @@ namespace Discord.API break; } } - public async Task ModifyMessageAsync(ulong channelId, ulong messageId, ModifyMessageParams args, RequestOptions options = null) + public async Task ModifyMessageAsync(ulong channelId, ulong messageId, Rest.ModifyMessageParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotEqual(messageId, 0, nameof(messageId)); diff --git a/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs index 4901ddc9d..c87d82c51 100644 --- a/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs @@ -8,5 +8,7 @@ namespace Discord.API.Rest { [JsonProperty("content")] public Optional Content { get; set; } + [JsonProperty("embed")] + public Optional Embed { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index a9dc4735c..c33b3e359 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Threading.Tasks; namespace Discord diff --git a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs new file mode 100644 index 000000000..cc05e620a --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Discord +{ + public class ModifyMessageParams + { + public Optional Content { get; set; } + public Optional Embed { get; set; } + } +} diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 358f6f5a9..91ca2ce1b 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -15,7 +15,12 @@ namespace Discord.Rest { var args = new ModifyMessageParams(); func(args); - return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyMessageParams + { + Content = args.Content, + Embed = args.Embed.IsSpecified ? args.Embed.Value.Build() : Optional.Create() + }; + return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) From 08b836797bd902b1e3cd10472856f2db145643b8 Mon Sep 17 00:00:00 2001 From: Sindre Langhus Date: Sat, 26 Nov 2016 04:43:29 +0100 Subject: [PATCH 030/263] Current timestamp namechange --- src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index aac361888..2fba6d47e 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -55,7 +55,7 @@ namespace Discord ImageUrl = ImageUrl; return this; } - public EmbedBuilder WithTimestamp() + public EmbedBuilder WithCurrentTimestamp() { Timestamp = DateTimeOffset.UtcNow; return this; From 1ed4f703bf61b9ab8af7c3d64d266154265fc236 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 14:59:20 -0500 Subject: [PATCH 031/263] Cache the current application on DiscordSocketClient --- .../Attributes/Preconditions/RequireOwnerAttribute.cs | 5 +---- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 7 +++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index beb8dfdd0..8992cd115 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -13,12 +13,9 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { - private IApplication application; - public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) { - if (application == null) - application = await context.Client.GetApplicationInfoAsync(); + var application = await context.Client.GetApplicationInfoAsync(); if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess(); return PreconditionResult.FromError("Command can only be run by the owner of the bot"); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 3eb4158d1..8c6a37f3c 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -38,6 +38,7 @@ namespace Discord.WebSocket private int _nextAudioId; private bool _canReconnect; private DateTimeOffset? _statusSince; + private RestApplication _application; /// Gets the shard of of this client. public int ShardId { get; } @@ -333,8 +334,10 @@ namespace Discord.WebSocket } /// - public Task GetApplicationInfoAsync() - => ClientHelper.GetApplicationInfoAsync(this); + public async Task GetApplicationInfoAsync() + { + return _application ?? (_application = await ClientHelper.GetApplicationInfoAsync(this)); + } /// public SocketGuild GetGuild(ulong id) From 1be6f77efb623878d085b1012adae0aeb06f06eb Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 21:22:35 -0500 Subject: [PATCH 032/263] Don't check message content if an embed is present when modifying --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index e280c730f..6ec7c5fb2 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -505,7 +505,8 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); if (args.Content.IsSpecified) { - Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); + if (!args.Embed.IsSpecified) + Preconditions.NotNullOrEmpty(args.Content, nameof(args.Content)); if (args.Content.Value.Length > DiscordConfig.MaxMessageSize) throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); } From 210c360fca85268c4697c4c4669af904f0e298af Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 21:32:21 -0500 Subject: [PATCH 033/263] Throw an exception when creating a Color with an invalid float value This prevents a 400 when sending a malformed color to Discord. --- src/Discord.Net.Core/Entities/Roles/Color.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index 563917959..d518d6edd 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -32,6 +32,12 @@ namespace Discord } public Color(float r, float g, float b) { + if (r <= 0.0f && r >= 1.0f) + throw new ArgumentOutOfRangeException(nameof(r), "A float value must be within [0,1]"); + if (g <= 0.0f || g >= 1.0f) + throw new ArgumentOutOfRangeException(nameof(g), "A float value must be within [0,1]"); + if (b <= 0.0f || b >= 1.0f) + throw new ArgumentOutOfRangeException(nameof(b), "A float value must be within [0,1]"); RawValue = ((uint)(r * 255.0f) << 16) | ((uint)(g * 255.0f) << 8) | From fb99b019a0c031fcb930394e32723df8d6793108 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 22:04:02 -0500 Subject: [PATCH 034/263] Fix issues with DefaultRunMode For a command to use the DefaultRunMode, it must now have it's RunMode set to RunMode.Default (this is the default value on CommandAttribute now). --- src/Discord.Net.Commands/Attributes/CommandAttribute.cs | 2 +- src/Discord.Net.Commands/Builders/CommandBuilder.cs | 2 +- src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- src/Discord.Net.Commands/RunMode.cs | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs index 86daf0103..5ae6092eb 100644 --- a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs @@ -6,7 +6,7 @@ namespace Discord.Commands public class CommandAttribute : Attribute { public string Text { get; } - public RunMode RunMode { get; set; } + public RunMode RunMode { get; set; } = RunMode.Default; public CommandAttribute() { diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index 25c0223e6..9b983fd1f 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -17,7 +17,7 @@ namespace Discord.Commands.Builders public string Name { get; set; } public string Summary { get; set; } public string Remarks { get; set; } - public RunMode? RunMode { get; set; } + public RunMode RunMode { get; set; } public int Priority { get; set; } public IReadOnlyList Preconditions => _preconditions; diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a9a3c2415..24f262482 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -37,7 +37,7 @@ namespace Discord.Commands Summary = builder.Summary; Remarks = builder.Remarks; - RunMode = builder.RunMode ?? service._defaultRunMode; + RunMode = (builder.RunMode == RunMode.Default ? service._defaultRunMode : builder.RunMode); Priority = builder.Priority; if (module.Aliases.Count != 0) diff --git a/src/Discord.Net.Commands/RunMode.cs b/src/Discord.Net.Commands/RunMode.cs index 0799f825c..2bb5dbbf6 100644 --- a/src/Discord.Net.Commands/RunMode.cs +++ b/src/Discord.Net.Commands/RunMode.cs @@ -2,6 +2,7 @@ { public enum RunMode { + Default, Sync, Mixed, Async From 9d4339e6952b10c010ec9f2e2197cc8a78aaf803 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 22:13:43 -0500 Subject: [PATCH 035/263] Fix a few typos in Color constraint logic --- src/Discord.Net.Core/Entities/Roles/Color.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index d518d6edd..ead46fd8a 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -32,11 +32,11 @@ namespace Discord } public Color(float r, float g, float b) { - if (r <= 0.0f && r >= 1.0f) + if (r < 0.0f || r > 1.0f) throw new ArgumentOutOfRangeException(nameof(r), "A float value must be within [0,1]"); - if (g <= 0.0f || g >= 1.0f) + if (g < 0.0f || g > 1.0f) throw new ArgumentOutOfRangeException(nameof(g), "A float value must be within [0,1]"); - if (b <= 0.0f || b >= 1.0f) + if (b < 0.0f || b > 1.0f) throw new ArgumentOutOfRangeException(nameof(b), "A float value must be within [0,1]"); RawValue = ((uint)(r * 255.0f) << 16) | From d74257cb6904611a442c56da795eb4c57342cf03 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 26 Nov 2016 23:51:23 -0400 Subject: [PATCH 036/263] Drop applicationinfo cache on logout, fixed RPC appinfo exception. --- src/Discord.Net.Rest/DiscordRestClient.cs | 13 +++++++++++-- src/Discord.Net.Rpc/DiscordRpcClient.cs | 9 ++++++--- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 5 +++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index f36c0fb06..8c1bd45da 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -8,6 +8,8 @@ namespace Discord.Rest { public class DiscordRestClient : BaseDiscordClient, IDiscordClient { + private RestApplication _applicationInfo; + public new RestSelfUser CurrentUser => base.CurrentUser as RestSelfUser; public DiscordRestClient() : this(new DiscordRestConfig()) { } @@ -21,10 +23,17 @@ namespace Discord.Rest base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); return Task.CompletedTask; } + protected override Task OnLogoutAsync() + { + _applicationInfo = null; + return Task.CompletedTask; + } /// - public Task GetApplicationInfoAsync() - => ClientHelper.GetApplicationInfoAsync(this); + public async Task GetApplicationInfoAsync() + { + return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this)); + } /// public Task GetChannelAsync(ulong id) diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 52fe6172f..3ca288b12 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace Discord.Rpc { - public partial class DiscordRpcClient : BaseDiscordClient + public partial class DiscordRpcClient : BaseDiscordClient, IDiscordClient { private readonly Logger _rpcLogger; private readonly JsonSerializer _serializer; @@ -33,7 +33,7 @@ namespace Discord.Rpc public new API.DiscordRpcApiClient ApiClient => base.ApiClient as API.DiscordRpcApiClient; public new RestSelfUser CurrentUser { get { return base.CurrentUser as RestSelfUser; } private set { base.CurrentUser = value; } } - public RestApplication CurrentApplication { get; private set; } + public RestApplication ApplicationInfo { get; private set; } /// Creates a new RPC discord client. public DiscordRpcClient(string clientId, string origin) @@ -393,7 +393,7 @@ namespace Discord.Rpc { var response = await ApiClient.SendAuthenticateAsync(options).ConfigureAwait(false); CurrentUser = RestSelfUser.Create(this, response.User); - CurrentApplication = RestApplication.Create(this, response.Application); + ApplicationInfo = RestApplication.Create(this, response.Application); Scopes = response.Scopes; TokenExpiresAt = response.Expires; @@ -547,5 +547,8 @@ namespace Discord.Rpc return; } } + + //IDiscordClient + Task IDiscordClient.GetApplicationInfoAsync() => Task.FromResult(ApplicationInfo); } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 8c6a37f3c..3e208ce58 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -38,7 +38,7 @@ namespace Discord.WebSocket private int _nextAudioId; private bool _canReconnect; private DateTimeOffset? _statusSince; - private RestApplication _application; + private RestApplication _applicationInfo; /// Gets the shard of of this client. public int ShardId { get; } @@ -124,6 +124,7 @@ namespace Discord.WebSocket if (ConnectionState != ConnectionState.Disconnected) await DisconnectInternalAsync(null, false).ConfigureAwait(false); + _applicationInfo = null; _voiceRegions = ImmutableDictionary.Create(); } @@ -336,7 +337,7 @@ namespace Discord.WebSocket /// public async Task GetApplicationInfoAsync() { - return _application ?? (_application = await ClientHelper.GetApplicationInfoAsync(this)); + return _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this)); } /// From ecc3d9c729add86cb6bc5acbb61fd01cbd44139f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 25 Nov 2016 23:56:20 -0500 Subject: [PATCH 037/263] Add configuration option for case insensitive commands Currently, commands are case-sensitive. This PR allows for commands to be case insensitive (which is now the default option). --- src/Discord.Net.Commands/CommandService.cs | 4 +++- src/Discord.Net.Commands/CommandServiceConfig.cs | 2 ++ src/Discord.Net.Commands/Info/CommandInfo.cs | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 9420476d5..26d811440 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -19,6 +19,7 @@ namespace Discord.Commands private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; + internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; public IEnumerable Modules => _moduleDefs.Select(x => x); @@ -67,6 +68,7 @@ namespace Discord.Commands [typeof(IGroupUser)] = new UserTypeReader(), [typeof(IGuildUser)] = new UserTypeReader(), }; + _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; } @@ -212,7 +214,7 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); public SearchResult Search(CommandContext context, string input) { - string lowerInput = input.ToLowerInvariant(); + input = _caseSensitive ? input : input.ToLowerInvariant(); var matches = _map.GetCommands(input).OrderByDescending(x => x.Priority).ToImmutableArray(); if (matches.Length > 0) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 97c98a54c..4ac79fe8f 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -4,5 +4,7 @@ { /// The default RunMode commands should have, if one is not specified on the Command attribute or builder. public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; + /// Should commands be case-sensitive? + public bool CaseSensitiveCommands { get; set; } = false; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 2ca8b1651..b4422e911 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -7,9 +7,11 @@ using System.Threading.Tasks; using System.Reflection; using Discord.Commands.Builders; +using System.Diagnostics; namespace Discord.Commands { + [DebuggerDisplay("{Name,nq}")] public class CommandInfo { private static readonly System.Reflection.MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList)); From d72122eef9d32d8a094effe2cd305ec3196a9e78 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 26 Nov 2016 23:15:36 -0500 Subject: [PATCH 038/263] Resolve conflicts. --- src/Discord.Net.Commands/Info/CommandInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index b4422e911..571c47e13 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -44,13 +44,13 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).ToImmutableArray(); + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // only module provides aliases else if (module.Aliases.Count > 0) - Aliases = module.Aliases.ToImmutableArray(); + Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // only command provides aliases else if (builder.Aliases.Count > 0) - Aliases = builder.Aliases.ToImmutableArray(); + Aliases = builder.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // neither provide aliases else throw new InvalidOperationException("Cannot build a command without any aliases"); From 2e1ec5803b88b82b948f880f6ee15bdd56a5eec3 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 27 Nov 2016 00:41:03 -0400 Subject: [PATCH 039/263] Added AddReactions to Permission constructors --- .../Permissions/ChannelPermissions.cs | 19 ++++++++++------- .../Entities/Permissions/GuildPermissions.cs | 21 +++++++++++-------- .../Permissions/OverwritePermissions.cs | 14 +++++++++---- .../Entities/Messages/RestUserMessage.cs | 4 ++-- .../Entities/Messages/SocketUserMessage.cs | 6 ++---- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 06635feed..93c030f4a 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -75,7 +75,8 @@ namespace Discord /// Creates a new ChannelPermissions with the provided packed value. public ChannelPermissions(ulong rawValue) { RawValue = rawValue; } - private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null, + private ChannelPermissions(ulong initialValue, bool? createInstantInvite = null, bool? manageChannel = null, + bool? addReactions = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, @@ -85,6 +86,7 @@ namespace Discord Permissions.SetValue(ref value, createInstantInvite, ChannelPermission.CreateInstantInvite); Permissions.SetValue(ref value, manageChannel, ChannelPermission.ManageChannel); + Permissions.SetValue(ref value, addReactions, ChannelPermission.AddReactions); Permissions.SetValue(ref value, readMessages, ChannelPermission.ReadMessages); Permissions.SetValue(ref value, sendMessages, ChannelPermission.SendMessages); Permissions.SetValue(ref value, sendTTSMessages, ChannelPermission.SendTTSMessages); @@ -106,23 +108,24 @@ namespace Discord } /// Creates a new ChannelPermissions with the provided permissions. - public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false, + public ChannelPermissions(bool createInstantInvite = false, bool manageChannel = false, + bool addReactions = false, bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false) - : this(0, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, - embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions) - { } + : this(0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, + embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, + speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions) { } /// Creates a new ChannelPermissions from this one, changing the provided non-null permissions. - public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, + public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, + bool? addReactions = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) - => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, + => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions); diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 92fca96bd..1badf7d30 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -78,7 +78,8 @@ namespace Discord public GuildPermissions(ulong rawValue) { RawValue = rawValue; } private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, - bool? banMembers = null, bool? administrator = null, bool? manageChannel = null, bool? manageGuild = null, + bool? banMembers = null, bool? administrator = null, bool? manageChannel = null, bool? manageGuild = null, + bool? addReactions = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? userExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, @@ -93,6 +94,7 @@ namespace Discord Permissions.SetValue(ref value, administrator, GuildPermission.Administrator); Permissions.SetValue(ref value, manageChannel, GuildPermission.ManageChannels); Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); + Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions); Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages); Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); @@ -120,28 +122,29 @@ namespace Discord /// Creates a new GuildPermissions with the provided permissions. public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false, + bool addReactions = false, bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, bool moveMembers = false, bool useVoiceActivation = false, bool? changeNickname = false, bool? manageNicknames = false, bool manageRoles = false, bool manageWebhooks = false, bool manageEmojis = false) - : this(0, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, readMessages, - sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, - manageWebhooks, manageEmojis) { } + : this(0, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions, + readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, + manageWebhooks, manageEmojis) { } /// Creates a new GuildPermissions from this one, changing the provided non-null permissions. public GuildPermissions Modify(bool? createInstantInvite = null, bool? kickMembers = null, bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, + bool? addReactions = null, bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, bool? moveMembers = null, bool? useVoiceActivation = null, bool? changeNickname = null, bool? manageNicknames = null, bool? manageRoles = null, bool? manageWebhooks = null, bool? manageEmojis = null) - => new GuildPermissions(RawValue, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, readMessages, - sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, - manageWebhooks, manageEmojis); + => new GuildPermissions(RawValue, createInstantInvite, manageRoles, kickMembers, banMembers, manageChannels, manageGuild, addReactions, + readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, mentionEveryone, useExternalEmojis, connect, + speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, + manageWebhooks, manageEmojis); public bool Has(GuildPermission permission) => Permissions.GetValue(RawValue, permission); diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs index 2fd0c2d93..f21155475 100644 --- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs @@ -24,6 +24,8 @@ namespace Discord public PermValue CreateInstantInvite => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.CreateInstantInvite); /// If Allowed, a user may create, delete and modify this channel. public PermValue ManageChannel => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageChannel); + /// If Allowed, a user may add reactions. + public PermValue AddReactions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.AddReactions); /// If Allowed, a user may join channels. public PermValue ReadMessages => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ReadMessages); /// If Allowed, a user may send messages. @@ -67,6 +69,7 @@ namespace Discord } private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null, + PermValue? addReactions = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, @@ -74,6 +77,7 @@ namespace Discord { Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); Permissions.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannel); + Permissions.SetValue(ref allowValue, ref denyValue, addReactions, ChannelPermission.AddReactions); Permissions.SetValue(ref allowValue, ref denyValue, readMessages, ChannelPermission.ReadMessages); Permissions.SetValue(ref allowValue, ref denyValue, sendMessages, ChannelPermission.SendMessages); Permissions.SetValue(ref allowValue, ref denyValue, sendTTSMessages, ChannelPermission.SendTTSMessages); @@ -96,22 +100,24 @@ namespace Discord } /// Creates a new ChannelPermissions with the provided permissions. - public OverwritePermissions(PermValue createInstantInvite = PermValue.Inherit, PermValue manageChannel = PermValue.Inherit, + public OverwritePermissions(PermValue createInstantInvite = PermValue.Inherit, PermValue manageChannel = PermValue.Inherit, + PermValue addReactions = PermValue.Inherit, PermValue readMessages = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit, PermValue embedLinks = PermValue.Inherit, PermValue attachFiles = PermValue.Inherit, PermValue readMessageHistory = PermValue.Inherit, PermValue mentionEveryone = PermValue.Inherit, PermValue useExternalEmojis = PermValue.Inherit, PermValue connect = PermValue.Inherit, PermValue speak = PermValue.Inherit, PermValue muteMembers = PermValue.Inherit, PermValue deafenMembers = PermValue.Inherit, PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue managePermissions = PermValue.Inherit) - : this(0, 0, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, + : this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions) { } /// Creates a new OverwritePermissions from this one, changing the provided non-null permissions. - public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null, + public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null, + PermValue? addReactions = null, PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null) - => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, readMessages, sendMessages, sendTTSMessages, manageMessages, + => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions); diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index b95253b8e..fc8792e19 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -1,8 +1,8 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Model = Discord.API.Message; diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index e8a1fa079..6184939dd 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -1,12 +1,10 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; -using System.Threading.Tasks; using System.Linq; -using Discord.API.Gateway; +using System.Threading.Tasks; using Model = Discord.API.Message; namespace Discord.WebSocket From f56a1b653da1e91f7b9d6043a051d35487c37cb2 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 27 Nov 2016 00:55:01 -0400 Subject: [PATCH 040/263] Cleaned up Reactions PR --- .../Entities/Messages/Emoji.cs | 4 +-- .../Entities/Messages/IReaction.cs | 7 +--- src/Discord.Net.Core/Utils/Preconditions.cs | 3 +- .../Entities/Messages/RestReaction.cs | 26 +++++++-------- .../Entities/Messages/RestUserMessage.cs | 2 +- .../{GatewayReaction.cs => Reaction.cs} | 8 ++--- .../DiscordSocketClient.cs | 8 ++--- .../Entities/Messages/SocketReaction.cs | 32 +++++++++---------- 8 files changed, 40 insertions(+), 50 deletions(-) rename src/Discord.Net.WebSocket/API/Gateway/{GatewayReaction.cs => Reaction.cs} (69%) diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index dddbe65f1..6faa3e1a4 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.Globalization; +using Model = Discord.API.Emoji; namespace Discord { @@ -18,8 +19,7 @@ namespace Discord Id = id; Name = name; } - - internal static Emoji FromApi(API.Emoji emoji) + internal static Emoji Create(Model emoji) { return new Emoji(emoji.Id.GetValueOrDefault(), emoji.Name); } diff --git a/src/Discord.Net.Core/Entities/Messages/IReaction.cs b/src/Discord.Net.Core/Entities/Messages/IReaction.cs index 66832760b..7145fce7f 100644 --- a/src/Discord.Net.Core/Entities/Messages/IReaction.cs +++ b/src/Discord.Net.Core/Entities/Messages/IReaction.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Discord +namespace Discord { public interface IReaction { diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index e88a006b8..14a730b7e 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -42,8 +42,7 @@ namespace Discord if (obj.Value == null) throw CreateNotNullException(name, msg); if (obj.Value.Trim().Length == 0) throw CreateNotEmptyException(name, msg); } - } - + } private static ArgumentException CreateNotEmptyException(string name, string msg) { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs index 7512dd4d8..d957c9145 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -1,22 +1,22 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Reaction; +using Model = Discord.API.Reaction; namespace Discord { public class RestReaction : IReaction { - internal RestReaction(Model model) + public Emoji Emoji { get; } + public int Count { get; } + public bool Me { get; } + + internal RestReaction(Emoji emoji, int count, bool me) { - Emoji = Emoji.FromApi(model.Emoji); - Count = model.Count; - Me = model.Me; + Emoji = emoji; + Count = count; + Me = me; + } + internal static RestReaction Create(Model model) + { + return new RestReaction(Emoji.Create(model.Emoji), model.Count, model.Me); } - - public Emoji Emoji { get; private set; } - public int Count { get; private set; } - public bool Me { get; private set; } } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index fc8792e19..b49aa3c67 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -109,7 +109,7 @@ namespace Discord.Rest { var reactions = ImmutableArray.CreateBuilder(value.Length); for (int i = 0; i < value.Length; i++) - reactions.Add(new RestReaction(value[i])); + reactions.Add(RestReaction.Create(value[i])); _reactions = reactions.ToImmutable(); } else diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs similarity index 69% rename from src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs rename to src/Discord.Net.WebSocket/API/Gateway/Reaction.cs index 2b9d6becc..2ed22a97b 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/GatewayReaction.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GatewayReaction + public class Reaction { [JsonProperty("user_id")] public ulong UserId { get; set; } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index fc3094871..5cb228506 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1312,13 +1312,13 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false); - var data = (payload as JToken).ToObject(_serializer); + var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); - SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); + SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); if (cachedMsg != null) { @@ -1339,13 +1339,13 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_REMOVE)").ConfigureAwait(false); - var data = (payload as JToken).ToObject(_serializer); + var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); - SocketReaction reaction = new SocketReaction(data, channel, Optional.Create(cachedMsg), Optional.Create(user)); + SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); if (cachedMsg != null) { cachedMsg.RemoveReaction(reaction); diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index c2e544a2c..3e56644b1 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -1,28 +1,28 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Model = Discord.API.Gateway.GatewayReaction; +using Model = Discord.API.Gateway.Reaction; namespace Discord.WebSocket { public class SocketReaction : IReaction { - internal SocketReaction(Model model, ISocketMessageChannel channel, Optional message, Optional user) + public ulong UserId { get; } + public Optional User { get; } + public ulong MessageId { get; } + public Optional Message { get; } + public ISocketMessageChannel Channel { get; } + public Emoji Emoji { get; } + + internal SocketReaction(ISocketMessageChannel channel, ulong messageId, Optional message, ulong userId, Optional user, Emoji emoji) { Channel = channel; + MessageId = messageId; Message = message; - MessageId = model.MessageId; + UserId = userId; User = user; - UserId = model.UserId; - Emoji = Emoji.FromApi(model.Emoji); + Emoji = emoji; + } + internal static SocketReaction Create(Model model, ISocketMessageChannel channel, Optional message, Optional user) + { + return new SocketReaction(channel, model.MessageId, message, model.UserId, user, Emoji.Create(model.Emoji)); } - - public ulong UserId { get; private set; } - public Optional User { get; private set; } - public ulong MessageId { get; private set; } - public Optional Message { get; private set; } - public ISocketMessageChannel Channel { get; private set; } - public Emoji Emoji { get; private set; } } } From 2a33bb0c122d12f7c642bc9399421a70bc865324 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 27 Nov 2016 01:07:19 -0400 Subject: [PATCH 041/263] Escaped amp in XML example. --- .../Attributes/Preconditions/RequireUserPermissionAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 17cf234aa..a5b04bd73 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -29,7 +29,7 @@ namespace Discord.Commands /// /// /// [Command("permission")] - /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)] + /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)] /// public async Task HasPermission() /// { /// await ReplyAsync("You can read messages and the message history!"); From 08ae9e4e86995a37fdcf9610d02290328f376f93 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 27 Nov 2016 01:13:29 -0400 Subject: [PATCH 042/263] Fixed logic in precondition attribute docstrings --- .../Preconditions/RequireBotPermissionAttribute.cs | 8 +++----- .../Attributes/Preconditions/RequireContextAttribute.cs | 2 +- .../Attributes/Preconditions/RequireOwnerAttribute.cs | 3 --- .../Preconditions/RequireUserPermissionAttribute.cs | 6 +++--- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs index 914978192..9f0e3c905 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -1,12 +1,10 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; namespace Discord.Commands { /// - /// This attribute requires that the bot has a speicifed permission in the channel a command is invoked in. + /// This attribute requires that the bot has a specified permission in the channel a command is invoked in. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireBotPermissionAttribute : PreconditionAttribute @@ -18,7 +16,7 @@ namespace Discord.Commands /// Require that the bot account has a specified GuildPermission /// /// This precondition will always fail if the command is being invoked in a private channel. - /// The GuildPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// The GuildPermission that the bot must have. Multiple permissions can be specified by ORing the permissions together. public RequireBotPermissionAttribute(GuildPermission permission) { GuildPermission = permission; @@ -27,7 +25,7 @@ namespace Discord.Commands /// /// Require that the bot account has a specified ChannelPermission. /// - /// The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// The ChannelPermission that the bot must have. Multiple permissions can be specified by ORing the permissions together. /// /// /// [Command("permission")] diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index beadbbc89..bc5d9af58 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -22,7 +22,7 @@ namespace Discord.Commands /// /// Require that the command be invoked in a specified context. /// - /// The type of context the command can be invoked in. Multiple contexts can be speicifed by ORing the contexts together. + /// The type of context the command can be invoked in. Multiple contexts can be specified by ORing the contexts together. /// /// /// [Command("private_only")] diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index 8992cd115..5b290954c 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -1,8 +1,5 @@ using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; -using Discord; namespace Discord.Commands { diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index a5b04bd73..04e221008 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -16,7 +16,7 @@ namespace Discord.Commands /// Require that the user invoking the command has a specified GuildPermission /// /// This precondition will always fail if the command is being invoked in a private channel. - /// The GuildPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// The GuildPermission that the user must have. Multiple permissions can be specified by ORing the permissions together. public RequireUserPermissionAttribute(GuildPermission permission) { GuildPermission = permission; @@ -25,11 +25,11 @@ namespace Discord.Commands /// /// Require that the user invoking the command has a specified ChannelPermission. /// - /// The ChannelPermission that the user must have. Multiple permissions can be specified by ORing or ANDing the permissions together. + /// The ChannelPermission that the user must have. Multiple permissions can be specified by ORing the permissions together. /// /// /// [Command("permission")] - /// [RequireUserPermission(ChannelPermission.ReadMessageHistory & ChannelPermission.ReadMessages)] + /// [RequireUserPermission(ChannelPermission.ReadMessageHistory | ChannelPermission.ReadMessages)] /// public async Task HasPermission() /// { /// await ReplyAsync("You can read messages and the message history!"); From 3e3566618647a0d00c91cae78bbe4bd2d42c9c4f Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 27 Nov 2016 01:57:12 -0800 Subject: [PATCH 043/263] Add TimeSpan TypeReader --- src/Discord.Net.Commands/CommandService.cs | 18 +++++++++--------- src/Discord.Net.Commands/PrimitiveParsers.cs | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 26d811440..b6659fea3 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -14,7 +14,7 @@ namespace Discord.Commands public class CommandService { private readonly SemaphoreSlim _moduleLock; - private readonly ConcurrentDictionary _typedModuleDefs; + private readonly ConcurrentDictionary _typedModuleDefs; private readonly ConcurrentDictionary _typeReaders; private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; @@ -50,7 +50,7 @@ namespace Discord.Commands [typeof(decimal)] = new SimpleTypeReader(), [typeof(DateTime)] = new SimpleTypeReader(), [typeof(DateTimeOffset)] = new SimpleTypeReader(), - + [typeof(TimeSpan)] = new SimpleTypeReader(), [typeof(IMessage)] = new MessageTypeReader(), [typeof(IUserMessage)] = new MessageTypeReader(), [typeof(IChannel)] = new ChannelTypeReader(), @@ -105,7 +105,7 @@ namespace Discord.Commands throw new InvalidOperationException($"Could not build the module {typeof(T).FullName}, did you pass an invalid type?"); _typedModuleDefs[module.Key] = module.Value; - + return LoadModuleInternal(module.Value); } finally @@ -143,7 +143,7 @@ namespace Discord.Commands foreach (var submodule in module.Submodules) LoadModuleInternal(submodule); - + return module; } @@ -168,7 +168,7 @@ namespace Discord.Commands _typedModuleDefs.TryGetValue(typeof(T), out module); if (module == default(ModuleInfo)) return false; - + return RemoveModuleInternal(module); } finally @@ -181,7 +181,7 @@ namespace Discord.Commands var defsRemove = module; if (!_moduleDefs.TryTake(out defsRemove)) return false; - + foreach (var cmd in module.Commands) _map.RemoveCommand(cmd); @@ -216,14 +216,14 @@ namespace Discord.Commands { input = _caseSensitive ? input : input.ToLowerInvariant(); var matches = _map.GetCommands(input).OrderByDescending(x => x.Priority).ToImmutableArray(); - + if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); else return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); } - public Task ExecuteAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public Task ExecuteAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) => ExecuteAsync(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling); public async Task ExecuteAsync(CommandContext context, string input, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { @@ -272,7 +272,7 @@ namespace Discord.Commands return await commands[i].Execute(context, parseResult, dependencyMap).ConfigureAwait(false); } - + return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."); } } diff --git a/src/Discord.Net.Commands/PrimitiveParsers.cs b/src/Discord.Net.Commands/PrimitiveParsers.cs index 5e3dcd68a..0b00a90e0 100644 --- a/src/Discord.Net.Commands/PrimitiveParsers.cs +++ b/src/Discord.Net.Commands/PrimitiveParsers.cs @@ -27,6 +27,7 @@ namespace Discord.Commands parserBuilder[typeof(decimal)] = (TryParseDelegate)decimal.TryParse; parserBuilder[typeof(DateTime)] = (TryParseDelegate)DateTime.TryParse; parserBuilder[typeof(DateTimeOffset)] = (TryParseDelegate)DateTimeOffset.TryParse; + parserBuilder[typeof(TimeSpan)] = (TryParseDelegate)TimeSpan.TryParse; parserBuilder[typeof(char)] = (TryParseDelegate)char.TryParse; parserBuilder[typeof(string)] = (TryParseDelegate)delegate (string str, out string value) { From 0771fcce63ee2ecfba0b69a0d99b1567379f8c4a Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 27 Nov 2016 01:28:11 -0800 Subject: [PATCH 044/263] Add Parent property to ModuleInfo --- src/Discord.Net.Commands/Builders/ModuleBuilder.cs | 11 +++++++---- src/Discord.Net.Commands/Info/ModuleInfo.cs | 12 ++++++++---- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 083de8e81..1a4f46663 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -14,8 +14,7 @@ namespace Discord.Commands.Builders public CommandService Service { get; } public ModuleBuilder Parent { get; } public string Name { get; set; } - public string Summary { get; set; } - public string Remarks { get; set; } + public string Summary { get; set; } public string Remarks { get; set; } public IReadOnlyList Commands => _commands; public IReadOnlyList Modules => _submodules; @@ -97,13 +96,17 @@ namespace Discord.Commands.Builders return this; } - public ModuleInfo Build(CommandService service) + private ModuleInfo BuildImpl(CommandService service, ModuleInfo parent = null) { //Default name to first alias if (Name == null) Name = _aliases[0]; - return new ModuleInfo(this, service); + return new ModuleInfo(this, service, parent); } + + public ModuleInfo Build(CommandService service) => BuildImpl(service); + + internal ModuleInfo Build(CommandService service, ModuleInfo parent) => BuildImpl(service, parent); } } diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 3baa2d34f..ad228baf1 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; @@ -17,14 +18,17 @@ namespace Discord.Commands public IEnumerable Commands { get; } public IReadOnlyList Preconditions { get; } public IReadOnlyList Submodules { get; } + public ModuleInfo Parent { get; } + public bool IsSubmodule => Parent == null; - internal ModuleInfo(ModuleBuilder builder, CommandService service) + internal ModuleInfo(ModuleBuilder builder, CommandService service, ModuleInfo parent = null) { Service = service; Name = builder.Name; Summary = builder.Summary; Remarks = builder.Remarks; + Parent = parent; Aliases = BuildAliases(builder).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)); @@ -67,13 +71,13 @@ namespace Discord.Commands return result; } - private static List BuildSubmodules(ModuleBuilder parent, CommandService service) + private List BuildSubmodules(ModuleBuilder parent, CommandService service) { var result = new List(); foreach (var submodule in parent.Modules) { - result.Add(submodule.Build(service)); + result.Add(submodule.Build(service, this)); } return result; @@ -93,4 +97,4 @@ namespace Discord.Commands return result; } } -} \ No newline at end of file +} From 686531807132413c3809e22b0fc1dddca2c1b9d1 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 27 Nov 2016 01:32:15 -0800 Subject: [PATCH 045/263] Fix IsSubmodule property's correctness --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index ad228baf1..ab4f65713 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -19,7 +19,7 @@ namespace Discord.Commands public IReadOnlyList Preconditions { get; } public IReadOnlyList Submodules { get; } public ModuleInfo Parent { get; } - public bool IsSubmodule => Parent == null; + public bool IsSubmodule => Parent != null; internal ModuleInfo(ModuleBuilder builder, CommandService service, ModuleInfo parent = null) { From 4896524d1a033299a453b782e11e0bcbe18f183c Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 27 Nov 2016 01:36:16 -0800 Subject: [PATCH 046/263] Limit CommandService.Modules to top level modules --- src/Discord.Net.Commands/CommandService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b6659fea3..3bac40634 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -22,7 +22,7 @@ namespace Discord.Commands internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; - public IEnumerable Modules => _moduleDefs.Select(x => x); + public IEnumerable Modules => _moduleDefs.Where(x => !x.IsSubmodule); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); public CommandService() : this(new CommandServiceConfig()) { } From 790ca10d1744ec38c2dc30d7f2cb4e6851e1d15c Mon Sep 17 00:00:00 2001 From: james7132 Date: Sun, 27 Nov 2016 01:41:48 -0800 Subject: [PATCH 047/263] Fix accidental line join --- src/Discord.Net.Commands/Builders/ModuleBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 1a4f46663..4a4b83497 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -14,7 +14,8 @@ namespace Discord.Commands.Builders public CommandService Service { get; } public ModuleBuilder Parent { get; } public string Name { get; set; } - public string Summary { get; set; } public string Remarks { get; set; } + public string Summary { get; set; } + public string Remarks { get; set; } public IReadOnlyList Commands => _commands; public IReadOnlyList Modules => _submodules; From 2c768c413a4e342a695dd6e3213e3c99380c3fb4 Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 30 Nov 2016 19:52:58 +0000 Subject: [PATCH 048/263] Revert CommandService.Modules change --- src/Discord.Net.Commands/CommandService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 3bac40634..b6659fea3 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -22,7 +22,7 @@ namespace Discord.Commands internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; - public IEnumerable Modules => _moduleDefs.Where(x => !x.IsSubmodule); + public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); public CommandService() : this(new CommandServiceConfig()) { } From 11f507e26afc838baaa9e51cdaa525bcdd550d67 Mon Sep 17 00:00:00 2001 From: Nikey646 Date: Thu, 1 Dec 2016 12:04:32 +1100 Subject: [PATCH 049/263] EmbedBuilder.WithImageUrl() now uses the value passed --- src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 2fba6d47e..40ef93e10 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -52,7 +52,7 @@ namespace Discord } public EmbedBuilder WithImageUrl(string imageUrl) { - ImageUrl = ImageUrl; + ImageUrl = imageUrl; return this; } public EmbedBuilder WithCurrentTimestamp() From 779aaffbf4006be593f560245bfcb9340ab0d740 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 3 Dec 2016 16:41:45 -0500 Subject: [PATCH 050/263] [docs] rewrite commands, update samples --- docs/api/.manifest | 2 +- docs/guides/commands.md | 223 ++++++++++++++-------- docs/guides/intro.md | 12 +- docs/guides/samples/command_handler.cs | 24 +-- docs/guides/samples/dependency_module.cs | 28 +-- docs/guides/samples/empty-module.cs | 6 + docs/guides/samples/faq/send_message.cs | 2 +- docs/guides/samples/groups.cs | 21 +- docs/guides/samples/logging.cs | 6 +- docs/guides/samples/require_context.cs | 10 - docs/guides/samples/require_owner.cs | 2 +- docs/guides/samples/require_permission.cs | 6 - docs/guides/samples/typereader.cs | 1 + docs/index.md | 2 +- 14 files changed, 210 insertions(+), 135 deletions(-) create mode 100644 docs/guides/samples/empty-module.cs delete mode 100644 docs/guides/samples/require_context.cs delete mode 100644 docs/guides/samples/require_permission.cs diff --git a/docs/api/.manifest b/docs/api/.manifest index dbdf6b4c0..9b0080765 100644 --- a/docs/api/.manifest +++ b/docs/api/.manifest @@ -1 +1 @@ -{"Discord.Commands":"Discord.Commands.yml","Discord.Commands.Command":"Discord.Commands.Command.yml","Discord.Commands.Command.Name":"Discord.Commands.Command.yml","Discord.Commands.Command.Description":"Discord.Commands.Command.yml","Discord.Commands.Command.Synopsis":"Discord.Commands.Command.yml","Discord.Commands.Command.Text":"Discord.Commands.Command.yml","Discord.Commands.Command.Module":"Discord.Commands.Command.yml","Discord.Commands.Command.Parameters":"Discord.Commands.Command.yml","Discord.Commands.Command.Parse(Discord.IMessage,Discord.Commands.SearchResult)":"Discord.Commands.Command.yml","Discord.Commands.Command.Execute(Discord.IMessage,Discord.Commands.ParseResult)":"Discord.Commands.Command.yml","Discord.Commands.Command.ToString":"Discord.Commands.Command.yml","Discord.Commands.CommandError":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnknownCommand":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ParseFailed":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.BadArgCount":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.CastFailed":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ObjectNotFound":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.MultipleMatches":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.Exception":"Discord.Commands.CommandError.yml","Discord.Commands.CommandParameter":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.Name":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.Description":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.IsOptional":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.IsRemainder":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.IsMultiple":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.Type":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.#ctor(System.String,System.String,System.Type,Discord.Commands.TypeReader,System.Boolean,System.Boolean,System.Boolean,System.Object)":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.Parse(Discord.IMessage,System.String)":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandParameter.ToString":"Discord.Commands.CommandParameter.yml","Discord.Commands.CommandService":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Modules":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Commands":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader``1(Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader(System.Type,Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Load(System.Object)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.LoadAssembly(System.Reflection.Assembly,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Unload(Discord.Commands.Module)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Unload(System.Object)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.IMessage,System.Int32)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.IMessage,System.String)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Execute(Discord.IMessage,System.Int32)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Execute(Discord.IMessage,System.String)":"Discord.Commands.CommandService.yml","Discord.Commands.Module":"Discord.Commands.Module.yml","Discord.Commands.Module.Service":"Discord.Commands.Module.yml","Discord.Commands.Module.Name":"Discord.Commands.Module.yml","Discord.Commands.Module.Commands":"Discord.Commands.Module.yml","Discord.Commands.Module.ToString":"Discord.Commands.Module.yml","Discord.Commands.CommandAttribute":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.Text":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor(System.String)":"Discord.Commands.CommandAttribute.yml","Discord.Commands.DescriptionAttribute":"Discord.Commands.DescriptionAttribute.yml","Discord.Commands.DescriptionAttribute.Text":"Discord.Commands.DescriptionAttribute.yml","Discord.Commands.DescriptionAttribute.#ctor(System.String)":"Discord.Commands.DescriptionAttribute.yml","Discord.Commands.SynopsisAttribute":"Discord.Commands.SynopsisAttribute.yml","Discord.Commands.SynopsisAttribute.Text":"Discord.Commands.SynopsisAttribute.yml","Discord.Commands.SynopsisAttribute.#ctor(System.String)":"Discord.Commands.SynopsisAttribute.yml","Discord.Commands.GroupAttribute":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.Prefix":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor(System.String)":"Discord.Commands.GroupAttribute.yml","Discord.Commands.ModuleAttribute":"Discord.Commands.ModuleAttribute.yml","Discord.Commands.ModuleAttribute.Prefix":"Discord.Commands.ModuleAttribute.yml","Discord.Commands.ModuleAttribute.AutoLoad":"Discord.Commands.ModuleAttribute.yml","Discord.Commands.ModuleAttribute.#ctor":"Discord.Commands.ModuleAttribute.yml","Discord.Commands.ModuleAttribute.#ctor(System.String)":"Discord.Commands.ModuleAttribute.yml","Discord.Commands.RemainderAttribute":"Discord.Commands.RemainderAttribute.yml","Discord.Commands.DependencyMap":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.#ctor":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Add``1(``0)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get``1":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get(System.Type)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet``1(``0@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.IDependencyMap":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Add``1(``0)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get``1":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet``1(``0@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get(System.Type)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.MessageExtensions":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasCharPrefix(Discord.IMessage,System.Char,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasStringPrefix(Discord.IMessage,System.String,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasMentionPrefix(Discord.IMessage,Discord.IUser,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.TypeReader":"Discord.Commands.TypeReader.yml","Discord.Commands.TypeReader.Read(Discord.IMessage,System.String)":"Discord.Commands.TypeReader.yml","Discord.Commands.ExecuteResult":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Exception":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Error":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ErrorReason":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.IsSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ToString":"Discord.Commands.ExecuteResult.yml","Discord.Commands.IResult":"Discord.Commands.IResult.yml","Discord.Commands.IResult.Error":"Discord.Commands.IResult.yml","Discord.Commands.IResult.ErrorReason":"Discord.Commands.IResult.yml","Discord.Commands.IResult.IsSuccess":"Discord.Commands.IResult.yml","Discord.Commands.ParseResult":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.Values":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.Error":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ErrorReason":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.IsSuccess":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ToString":"Discord.Commands.ParseResult.yml","Discord.Commands.SearchResult":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Text":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Commands":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Error":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ErrorReason":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.IsSuccess":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ToString":"Discord.Commands.SearchResult.yml","Discord.Commands.TypeReaderResult":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Value":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Error":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ErrorReason":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.IsSuccess":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Object)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ToString":"Discord.Commands.TypeReaderResult.yml","Discord":"Discord.yml","Discord.ConnectionState":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnected":"Discord.ConnectionState.yml","Discord.ConnectionState.Connecting":"Discord.ConnectionState.yml","Discord.ConnectionState.Connected":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnecting":"Discord.ConnectionState.yml","Discord.DiscordConfig":"Discord.DiscordConfig.yml","Discord.DiscordConfig.APIVersion":"Discord.DiscordConfig.yml","Discord.DiscordConfig.Version":"Discord.DiscordConfig.yml","Discord.DiscordConfig.ClientAPIUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.CDNUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.InviteUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessageSize":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessagesPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxUsersPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.LogLevel":"Discord.DiscordConfig.yml","Discord.Format":"Discord.Format.yml","Discord.Format.Bold(System.String)":"Discord.Format.yml","Discord.Format.Italics(System.String)":"Discord.Format.yml","Discord.Format.Underline(System.String)":"Discord.Format.yml","Discord.Format.Strikethrough(System.String)":"Discord.Format.yml","Discord.Format.Code(System.String,System.String)":"Discord.Format.yml","Discord.IDiscordClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectionState":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ApiClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.LogManager":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.DisconnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetApplicationInfoAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetChannelAsync(System.UInt64)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetPrivateChannelsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetConnectionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildAsync(System.UInt64)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildSummariesAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetInviteAsync(System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.UInt64)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.String,System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetCurrentUserAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.QueryUsersAsync(System.String,System.Int32)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionAsync(System.String)":"Discord.IDiscordClient.yml","Discord.LoginState":"Discord.LoginState.yml","Discord.LoginState.LoggedOut":"Discord.LoginState.yml","Discord.LoginState.LoggingIn":"Discord.LoginState.yml","Discord.LoginState.LoggedIn":"Discord.LoginState.yml","Discord.LoginState.LoggingOut":"Discord.LoginState.yml","Discord.LogSeverity":"Discord.LogSeverity.yml","Discord.LogSeverity.Critical":"Discord.LogSeverity.yml","Discord.LogSeverity.Error":"Discord.LogSeverity.yml","Discord.LogSeverity.Warning":"Discord.LogSeverity.yml","Discord.LogSeverity.Info":"Discord.LogSeverity.yml","Discord.LogSeverity.Verbose":"Discord.LogSeverity.yml","Discord.LogSeverity.Debug":"Discord.LogSeverity.yml","Discord.RequestOptions":"Discord.RequestOptions.yml","Discord.RequestOptions.Default":"Discord.RequestOptions.yml","Discord.RequestOptions.Timeout":"Discord.RequestOptions.yml","Discord.RequestOptions.#ctor":"Discord.RequestOptions.yml","Discord.TokenType":"Discord.TokenType.yml","Discord.TokenType.User":"Discord.TokenType.yml","Discord.TokenType.Bearer":"Discord.TokenType.yml","Discord.TokenType.Bot":"Discord.TokenType.yml","Discord.IApplication":"Discord.IApplication.yml","Discord.IApplication.Name":"Discord.IApplication.yml","Discord.IApplication.Description":"Discord.IApplication.yml","Discord.IApplication.RPCOrigins":"Discord.IApplication.yml","Discord.IApplication.Flags":"Discord.IApplication.yml","Discord.IApplication.IconUrl":"Discord.IApplication.yml","Discord.IApplication.Owner":"Discord.IApplication.yml","Discord.IDeletable":"Discord.IDeletable.yml","Discord.IDeletable.DeleteAsync":"Discord.IDeletable.yml","Discord.IEntity`1":"Discord.IEntity`1.yml","Discord.IEntity`1.Id":"Discord.IEntity`1.yml","Discord.IEntity`1.IsAttached":"Discord.IEntity`1.yml","Discord.IMentionable":"Discord.IMentionable.yml","Discord.IMentionable.Mention":"Discord.IMentionable.yml","Discord.ISnowflakeEntity":"Discord.ISnowflakeEntity.yml","Discord.ISnowflakeEntity.CreatedAt":"Discord.ISnowflakeEntity.yml","Discord.IUpdateable":"Discord.IUpdateable.yml","Discord.IUpdateable.UpdateAsync":"Discord.IUpdateable.yml","Discord.ChannelType":"Discord.ChannelType.yml","Discord.ChannelType.Text":"Discord.ChannelType.yml","Discord.ChannelType.DM":"Discord.ChannelType.yml","Discord.ChannelType.Voice":"Discord.ChannelType.yml","Discord.ChannelType.Group":"Discord.ChannelType.yml","Discord.IChannel":"Discord.IChannel.yml","Discord.IChannel.GetUsersAsync":"Discord.IChannel.yml","Discord.IChannel.GetUserAsync(System.UInt64)":"Discord.IChannel.yml","Discord.IDMChannel":"Discord.IDMChannel.yml","Discord.IDMChannel.Recipient":"Discord.IDMChannel.yml","Discord.IDMChannel.CloseAsync":"Discord.IDMChannel.yml","Discord.IGroupChannel":"Discord.IGroupChannel.yml","Discord.IGroupChannel.AddUserAsync(Discord.IUser)":"Discord.IGroupChannel.yml","Discord.IGroupChannel.LeaveAsync":"Discord.IGroupChannel.yml","Discord.IGuildChannel":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Name":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Position":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Guild":"Discord.IGuildChannel.yml","Discord.IGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetInvitesAsync":"Discord.IGuildChannel.yml","Discord.IGuildChannel.PermissionOverwrites":"Discord.IGuildChannel.yml","Discord.IGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams})":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUsersAsync":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUserAsync(System.UInt64)":"Discord.IGuildChannel.yml","Discord.IMessageChannel":"Discord.IMessageChannel.yml","Discord.IMessageChannel.CachedMessages":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.String,System.String,System.Boolean)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessageAsync(System.UInt64)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetCachedMessage(System.UInt64)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.Int32)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetPinnedMessagesAsync":"Discord.IMessageChannel.yml","Discord.IMessageChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage})":"Discord.IMessageChannel.yml","Discord.IMessageChannel.TriggerTypingAsync":"Discord.IMessageChannel.yml","Discord.IPrivateChannel":"Discord.IPrivateChannel.yml","Discord.IPrivateChannel.Recipients":"Discord.IPrivateChannel.yml","Discord.ITextChannel":"Discord.ITextChannel.yml","Discord.ITextChannel.Topic":"Discord.ITextChannel.yml","Discord.ITextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams})":"Discord.ITextChannel.yml","Discord.IVoiceChannel":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.Bitrate":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.UserLimit":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams})":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ConnectAsync":"Discord.IVoiceChannel.yml","Discord.DefaultMessageNotifications":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.MentionsOnly":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.AllMessages":"Discord.DefaultMessageNotifications.yml","Discord.Emoji":"Discord.Emoji.yml","Discord.Emoji.Id":"Discord.Emoji.yml","Discord.Emoji.Name":"Discord.Emoji.yml","Discord.Emoji.IsManaged":"Discord.Emoji.yml","Discord.Emoji.RequireColons":"Discord.Emoji.yml","Discord.Emoji.RoleIds":"Discord.Emoji.yml","Discord.Emoji.#ctor(Discord.API.Emoji)":"Discord.Emoji.yml","Discord.GuildEmbed":"Discord.GuildEmbed.yml","Discord.GuildEmbed.IsEnabled":"Discord.GuildEmbed.yml","Discord.GuildEmbed.ChannelId":"Discord.GuildEmbed.yml","Discord.GuildEmbed.#ctor(System.Boolean,System.Nullable{System.UInt64})":"Discord.GuildEmbed.yml","Discord.IGuild":"Discord.IGuild.yml","Discord.IGuild.Name":"Discord.IGuild.yml","Discord.IGuild.AFKTimeout":"Discord.IGuild.yml","Discord.IGuild.IsEmbeddable":"Discord.IGuild.yml","Discord.IGuild.DefaultMessageNotifications":"Discord.IGuild.yml","Discord.IGuild.MfaLevel":"Discord.IGuild.yml","Discord.IGuild.VerificationLevel":"Discord.IGuild.yml","Discord.IGuild.IconUrl":"Discord.IGuild.yml","Discord.IGuild.SplashUrl":"Discord.IGuild.yml","Discord.IGuild.Available":"Discord.IGuild.yml","Discord.IGuild.AFKChannelId":"Discord.IGuild.yml","Discord.IGuild.DefaultChannelId":"Discord.IGuild.yml","Discord.IGuild.EmbedChannelId":"Discord.IGuild.yml","Discord.IGuild.OwnerId":"Discord.IGuild.yml","Discord.IGuild.VoiceRegionId":"Discord.IGuild.yml","Discord.IGuild.AudioClient":"Discord.IGuild.yml","Discord.IGuild.EveryoneRole":"Discord.IGuild.yml","Discord.IGuild.Emojis":"Discord.IGuild.yml","Discord.IGuild.Features":"Discord.IGuild.yml","Discord.IGuild.Roles":"Discord.IGuild.yml","Discord.IGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams})":"Discord.IGuild.yml","Discord.IGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams})":"Discord.IGuild.yml","Discord.IGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams})":"Discord.IGuild.yml","Discord.IGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams})":"Discord.IGuild.yml","Discord.IGuild.LeaveAsync":"Discord.IGuild.yml","Discord.IGuild.GetBansAsync":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(Discord.IUser,System.Int32)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(System.UInt64,System.Int32)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(Discord.IUser)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.GetChannelsAsync":"Discord.IGuild.yml","Discord.IGuild.GetChannelAsync(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.CreateTextChannelAsync(System.String)":"Discord.IGuild.yml","Discord.IGuild.CreateVoiceChannelAsync(System.String)":"Discord.IGuild.yml","Discord.IGuild.GetInvitesAsync":"Discord.IGuild.yml","Discord.IGuild.GetRole(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean)":"Discord.IGuild.yml","Discord.IGuild.GetUsersAsync":"Discord.IGuild.yml","Discord.IGuild.GetUserAsync(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.GetCurrentUserAsync":"Discord.IGuild.yml","Discord.IGuild.DownloadUsersAsync":"Discord.IGuild.yml","Discord.IGuild.PruneUsersAsync(System.Int32,System.Boolean)":"Discord.IGuild.yml","Discord.IGuildIntegration":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Id":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Name":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Type":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsEnabled":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsSyncing":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireBehavior":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireGracePeriod":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.SyncedAt":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Account":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Guild":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.User":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Role":"Discord.IGuildIntegration.yml","Discord.IntegrationAccount":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Id":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Name":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.ToString":"Discord.IntegrationAccount.yml","Discord.IUserGuild":"Discord.IUserGuild.yml","Discord.IUserGuild.Name":"Discord.IUserGuild.yml","Discord.IUserGuild.IconUrl":"Discord.IUserGuild.yml","Discord.IUserGuild.IsOwner":"Discord.IUserGuild.yml","Discord.IUserGuild.Permissions":"Discord.IUserGuild.yml","Discord.IVoiceRegion":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Id":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Name":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsVip":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsOptimal":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SampleHostname":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SamplePort":"Discord.IVoiceRegion.yml","Discord.MfaLevel":"Discord.MfaLevel.yml","Discord.MfaLevel.Disabled":"Discord.MfaLevel.yml","Discord.MfaLevel.Enabled":"Discord.MfaLevel.yml","Discord.VerificationLevel":"Discord.VerificationLevel.yml","Discord.VerificationLevel.None":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Low":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Medium":"Discord.VerificationLevel.yml","Discord.VerificationLevel.High":"Discord.VerificationLevel.yml","Discord.IInvite":"Discord.IInvite.yml","Discord.IInvite.Code":"Discord.IInvite.yml","Discord.IInvite.Url":"Discord.IInvite.yml","Discord.IInvite.ChannelId":"Discord.IInvite.yml","Discord.IInvite.GuildId":"Discord.IInvite.yml","Discord.IInvite.AcceptAsync":"Discord.IInvite.yml","Discord.IInviteMetadata":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Inviter":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsRevoked":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsTemporary":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxAge":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxUses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Uses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.CreatedAt":"Discord.IInviteMetadata.yml","Discord.Direction":"Discord.Direction.yml","Discord.Direction.Before":"Discord.Direction.yml","Discord.Direction.After":"Discord.Direction.yml","Discord.Direction.Around":"Discord.Direction.yml","Discord.EmbedProvider":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Name":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Url":"Discord.EmbedProvider.yml","Discord.EmbedProvider.#ctor(System.String,System.String)":"Discord.EmbedProvider.yml","Discord.EmbedThumbnail":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Url":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ProxyUrl":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Height":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Width":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.#ctor(System.String,System.String,System.Nullable{System.Int32},System.Nullable{System.Int32})":"Discord.EmbedThumbnail.yml","Discord.IAttachment":"Discord.IAttachment.yml","Discord.IAttachment.Id":"Discord.IAttachment.yml","Discord.IAttachment.Filename":"Discord.IAttachment.yml","Discord.IAttachment.Url":"Discord.IAttachment.yml","Discord.IAttachment.ProxyUrl":"Discord.IAttachment.yml","Discord.IAttachment.Size":"Discord.IAttachment.yml","Discord.IAttachment.Height":"Discord.IAttachment.yml","Discord.IAttachment.Width":"Discord.IAttachment.yml","Discord.IEmbed":"Discord.IEmbed.yml","Discord.IEmbed.Url":"Discord.IEmbed.yml","Discord.IEmbed.Type":"Discord.IEmbed.yml","Discord.IEmbed.Title":"Discord.IEmbed.yml","Discord.IEmbed.Description":"Discord.IEmbed.yml","Discord.IEmbed.Provider":"Discord.IEmbed.yml","Discord.IEmbed.Thumbnail":"Discord.IEmbed.yml","Discord.IMessage":"Discord.IMessage.yml","Discord.IMessage.EditedTimestamp":"Discord.IMessage.yml","Discord.IMessage.IsTTS":"Discord.IMessage.yml","Discord.IMessage.IsPinned":"Discord.IMessage.yml","Discord.IMessage.Content":"Discord.IMessage.yml","Discord.IMessage.Timestamp":"Discord.IMessage.yml","Discord.IMessage.Type":"Discord.IMessage.yml","Discord.IMessage.Channel":"Discord.IMessage.yml","Discord.IMessage.Author":"Discord.IMessage.yml","Discord.IMessage.Attachments":"Discord.IMessage.yml","Discord.IMessage.Embeds":"Discord.IMessage.yml","Discord.IMessage.MentionedChannelIds":"Discord.IMessage.yml","Discord.IMessage.MentionedRoles":"Discord.IMessage.yml","Discord.IMessage.MentionedUsers":"Discord.IMessage.yml","Discord.IMessage.ModifyAsync(System.Action{Discord.API.Rest.ModifyMessageParams})":"Discord.IMessage.yml","Discord.IMessage.PinAsync":"Discord.IMessage.yml","Discord.IMessage.UnpinAsync":"Discord.IMessage.yml","Discord.IMessage.Resolve(System.Int32,System.Int32,Discord.UserResolveMode)":"Discord.IMessage.yml","Discord.IMessage.Resolve(Discord.UserResolveMode)":"Discord.IMessage.yml","Discord.MessageType":"Discord.MessageType.yml","Discord.MessageType.Default":"Discord.MessageType.yml","Discord.MessageType.RecipientAdd":"Discord.MessageType.yml","Discord.MessageType.RecipientRemove":"Discord.MessageType.yml","Discord.MessageType.Call":"Discord.MessageType.yml","Discord.MessageType.ChannelNameChange":"Discord.MessageType.yml","Discord.MessageType.ChannelIconChange":"Discord.MessageType.yml","Discord.UserResolveMode":"Discord.UserResolveMode.yml","Discord.UserResolveMode.NameOnly":"Discord.UserResolveMode.yml","Discord.UserResolveMode.NameAndDiscriminator":"Discord.UserResolveMode.yml","Discord.ChannelPermission":"Discord.ChannelPermission.yml","Discord.ChannelPermission.CreateInstantInvite":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageChannel":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendTTSMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.EmbedLinks":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AttachFiles":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessageHistory":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MentionEveryone":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Connect":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Speak":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MuteMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.DeafenMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MoveMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseVAD":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManagePermissions":"Discord.ChannelPermission.yml","Discord.ChannelPermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.None":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.All(Discord.IChannel)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.RawValue":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.CreateInstantInvite":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageChannel":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendTTSMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.EmbedLinks":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AttachFiles":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessageHistory":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MentionEveryone":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Connect":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Speak":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MuteMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.DeafenMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MoveMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseVAD":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManagePermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.UInt64)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToList":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToString":"Discord.ChannelPermissions.yml","Discord.GuildPermission":"Discord.GuildPermission.yml","Discord.GuildPermission.CreateInstantInvite":"Discord.GuildPermission.yml","Discord.GuildPermission.KickMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.BanMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.Administrator":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageChannels":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageGuild":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendTTSMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.EmbedLinks":"Discord.GuildPermission.yml","Discord.GuildPermission.AttachFiles":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessageHistory":"Discord.GuildPermission.yml","Discord.GuildPermission.MentionEveryone":"Discord.GuildPermission.yml","Discord.GuildPermission.Connect":"Discord.GuildPermission.yml","Discord.GuildPermission.Speak":"Discord.GuildPermission.yml","Discord.GuildPermission.MuteMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.DeafenMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.MoveMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.UseVAD":"Discord.GuildPermission.yml","Discord.GuildPermission.ChangeNickname":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageNicknames":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageRoles":"Discord.GuildPermission.yml","Discord.GuildPermissions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.None":"Discord.GuildPermissions.yml","Discord.GuildPermissions.All":"Discord.GuildPermissions.yml","Discord.GuildPermissions.RawValue":"Discord.GuildPermissions.yml","Discord.GuildPermissions.CreateInstantInvite":"Discord.GuildPermissions.yml","Discord.GuildPermissions.BanMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.KickMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Administrator":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageChannels":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageGuild":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendTTSMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.EmbedLinks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AttachFiles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessageHistory":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MentionEveryone":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Connect":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Speak":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MuteMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.DeafenMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MoveMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseVAD":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ChangeNickname":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageNicknames":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageRoles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.UInt64)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToList":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToString":"Discord.GuildPermissions.yml","Discord.Overwrite":"Discord.Overwrite.yml","Discord.Overwrite.TargetId":"Discord.Overwrite.yml","Discord.Overwrite.TargetType":"Discord.Overwrite.yml","Discord.Overwrite.Permissions":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(System.UInt64,Discord.PermissionTarget,Discord.OverwritePermissions)":"Discord.Overwrite.yml","Discord.OverwritePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.InheritAll":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.CreateInstantInvite":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageChannel":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendTTSMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.EmbedLinks":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AttachFiles":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessageHistory":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MentionEveryone":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Connect":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Speak":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MuteMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DeafenMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MoveMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseVAD":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManagePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(System.UInt64,System.UInt64)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Modify(System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue})":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToAllowList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToDenyList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToString":"Discord.OverwritePermissions.yml","Discord.PermissionTarget":"Discord.PermissionTarget.yml","Discord.PermissionTarget.Role":"Discord.PermissionTarget.yml","Discord.PermissionTarget.User":"Discord.PermissionTarget.yml","Discord.PermValue":"Discord.PermValue.yml","Discord.PermValue.Allow":"Discord.PermValue.yml","Discord.PermValue.Deny":"Discord.PermValue.yml","Discord.PermValue.Inherit":"Discord.PermValue.yml","Discord.Color":"Discord.Color.yml","Discord.Color.Default":"Discord.Color.yml","Discord.Color.RawValue":"Discord.Color.yml","Discord.Color.R":"Discord.Color.yml","Discord.Color.G":"Discord.Color.yml","Discord.Color.B":"Discord.Color.yml","Discord.Color.#ctor(System.UInt32)":"Discord.Color.yml","Discord.Color.#ctor(System.Byte,System.Byte,System.Byte)":"Discord.Color.yml","Discord.Color.#ctor(System.Single,System.Single,System.Single)":"Discord.Color.yml","Discord.Color.ToString":"Discord.Color.yml","Discord.IRole":"Discord.IRole.yml","Discord.IRole.Color":"Discord.IRole.yml","Discord.IRole.IsHoisted":"Discord.IRole.yml","Discord.IRole.IsManaged":"Discord.IRole.yml","Discord.IRole.Name":"Discord.IRole.yml","Discord.IRole.Permissions":"Discord.IRole.yml","Discord.IRole.Position":"Discord.IRole.yml","Discord.IRole.GuildId":"Discord.IRole.yml","Discord.IRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams})":"Discord.IRole.yml","Discord.Game":"Discord.Game.yml","Discord.Game.Name":"Discord.Game.yml","Discord.Game.StreamUrl":"Discord.Game.yml","Discord.Game.StreamType":"Discord.Game.yml","Discord.Game.#ctor(System.String,System.String,Discord.StreamType)":"Discord.Game.yml","Discord.Game.#ctor(System.String)":"Discord.Game.yml","Discord.Game.ToString":"Discord.Game.yml","Discord.IConnection":"Discord.IConnection.yml","Discord.IConnection.Id":"Discord.IConnection.yml","Discord.IConnection.Type":"Discord.IConnection.yml","Discord.IConnection.Name":"Discord.IConnection.yml","Discord.IConnection.IsRevoked":"Discord.IConnection.yml","Discord.IConnection.IntegrationIds":"Discord.IConnection.yml","Discord.IGroupUser":"Discord.IGroupUser.yml","Discord.IGroupUser.KickAsync":"Discord.IGroupUser.yml","Discord.IGroupUser.CreateDMChannelAsync":"Discord.IGroupUser.yml","Discord.IGuildUser":"Discord.IGuildUser.yml","Discord.IGuildUser.JoinedAt":"Discord.IGuildUser.yml","Discord.IGuildUser.Nickname":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildPermissions":"Discord.IGuildUser.yml","Discord.IGuildUser.Guild":"Discord.IGuildUser.yml","Discord.IGuildUser.Roles":"Discord.IGuildUser.yml","Discord.IGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.IGuildUser.yml","Discord.IGuildUser.KickAsync":"Discord.IGuildUser.yml","Discord.IGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams})":"Discord.IGuildUser.yml","Discord.IGuildUser.CreateDMChannelAsync":"Discord.IGuildUser.yml","Discord.IPresence":"Discord.IPresence.yml","Discord.IPresence.Game":"Discord.IPresence.yml","Discord.IPresence.Status":"Discord.IPresence.yml","Discord.ISelfUser":"Discord.ISelfUser.yml","Discord.ISelfUser.Email":"Discord.ISelfUser.yml","Discord.ISelfUser.IsVerified":"Discord.ISelfUser.yml","Discord.ISelfUser.IsMfaEnabled":"Discord.ISelfUser.yml","Discord.ISelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams})":"Discord.ISelfUser.yml","Discord.ISelfUser.ModifyStatusAsync(System.Action{Discord.API.Rest.ModifyPresenceParams})":"Discord.ISelfUser.yml","Discord.IUser":"Discord.IUser.yml","Discord.IUser.AvatarUrl":"Discord.IUser.yml","Discord.IUser.Discriminator":"Discord.IUser.yml","Discord.IUser.DiscriminatorValue":"Discord.IUser.yml","Discord.IUser.IsBot":"Discord.IUser.yml","Discord.IUser.Username":"Discord.IUser.yml","Discord.IVoiceState":"Discord.IVoiceState.yml","Discord.IVoiceState.IsDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSuppressed":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceChannel":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceSessionId":"Discord.IVoiceState.yml","Discord.StreamType":"Discord.StreamType.yml","Discord.StreamType.NotStreaming":"Discord.StreamType.yml","Discord.StreamType.Twitch":"Discord.StreamType.yml","Discord.UserStatus":"Discord.UserStatus.yml","Discord.UserStatus.Unknown":"Discord.UserStatus.yml","Discord.UserStatus.Online":"Discord.UserStatus.yml","Discord.UserStatus.Idle":"Discord.UserStatus.yml","Discord.UserStatus.Offline":"Discord.UserStatus.yml","Discord.DiscordClientExtensions":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetOptimalVoiceRegionAsync(Discord.Rest.DiscordRestClient)":"Discord.DiscordClientExtensions.yml","Discord.GuildExtensions":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetAFKChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetDefaultChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetEmbedChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetOwnerAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildUserExtensions":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.LogMessage":"Discord.LogMessage.yml","Discord.LogMessage.Severity":"Discord.LogMessage.yml","Discord.LogMessage.Source":"Discord.LogMessage.yml","Discord.LogMessage.Message":"Discord.LogMessage.yml","Discord.LogMessage.Exception":"Discord.LogMessage.yml","Discord.LogMessage.#ctor(Discord.LogSeverity,System.String,System.String,System.Exception)":"Discord.LogMessage.yml","Discord.LogMessage.ToString":"Discord.LogMessage.yml","Discord.LogMessage.ToString(System.Text.StringBuilder,System.Boolean,System.Boolean,System.DateTimeKind,System.Nullable{System.Int32})":"Discord.LogMessage.yml","Discord.RpcException":"Discord.RpcException.yml","Discord.RpcException.ErrorCode":"Discord.RpcException.yml","Discord.RpcException.Reason":"Discord.RpcException.yml","Discord.RpcException.#ctor(System.Int32,System.String)":"Discord.RpcException.yml","Discord.MentionUtils":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseUser(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseUser(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseChannel(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseChannel(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseRole(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseRole(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.Optional`1":"Discord.Optional`1.yml","Discord.Optional`1.Unspecified":"Discord.Optional`1.yml","Discord.Optional`1.Value":"Discord.Optional`1.yml","Discord.Optional`1.IsSpecified":"Discord.Optional`1.yml","Discord.Optional`1.#ctor(`0)":"Discord.Optional`1.yml","Discord.Optional`1.GetValueOrDefault":"Discord.Optional`1.yml","Discord.Optional`1.GetValueOrDefault(`0)":"Discord.Optional`1.yml","Discord.Optional`1.Equals(System.Object)":"Discord.Optional`1.yml","Discord.Optional`1.GetHashCode":"Discord.Optional`1.yml","Discord.Optional`1.ToString":"Discord.Optional`1.yml","Discord.Optional`1.op_Implicit(`0)~Discord.Optional{`0}":"Discord.Optional`1.yml","Discord.Optional`1.op_Explicit(Discord.Optional{`0})~`0":"Discord.Optional`1.yml","Discord.Optional":"Discord.Optional.yml","Discord.Optional.Create``1":"Discord.Optional.yml","Discord.Optional.Create``1(``0)":"Discord.Optional.yml","Discord.DiscordSocketConfig":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.GatewayEncoding":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.ShardId":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.TotalShards":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.MessageCacheSize":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.LargeThreshold":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.AudioMode":"Discord.DiscordSocketConfig.yml","Discord.DiscordSocketConfig.WebSocketProvider":"Discord.DiscordSocketConfig.yml","Discord.Audio":"Discord.Audio.yml","Discord.Audio.DiscordVoiceAPIClient":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.MaxBitrate":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Mode":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentRequest":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentGatewayMessage":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentDiscovery":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentData":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedEvent":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedPacket":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Disconnected":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.GuildId":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectionState":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Dispose":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(Discord.API.Voice.VoiceOpCode,System.Object,Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(System.Byte[],System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendHeartbeatAsync(Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendIdentityAsync(System.UInt64,System.String,System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSelectProtocol(System.String,System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSetSpeaking(System.Boolean)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectAsync(System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.DisconnectAsync":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendDiscoveryAsync(System.UInt32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SetUdpEndpoint(System.Net.IPEndPoint)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.AudioMode":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Disabled":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Outgoing":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Incoming":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Both":"Discord.Audio.AudioMode.yml","Discord.Audio.IAudioClient":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Connected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Disconnected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.LatencyUpdated":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.ApiClient":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.ConnectionState":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Latency":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.DisconnectAsync":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreatePCMStream(System.Int32,System.Nullable{System.Int32},Discord.Audio.OpusApplication,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.OpusApplication":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.Voice":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.MusicOrMixed":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.LowLatency":"Discord.Audio.OpusApplication.yml","Discord.Audio.SecretBox":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Encrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Decrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.OpusDecodeStream":"Discord.Audio.OpusDecodeStream.yml","Discord.Audio.OpusDecodeStream.Read(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.OpusDecodeStream.yml","Discord.Audio.OpusDecodeStream.Dispose(System.Boolean)":"Discord.Audio.OpusDecodeStream.yml","Discord.Audio.OpusEncodeStream":"Discord.Audio.OpusEncodeStream.yml","Discord.Audio.OpusEncodeStream.SampleRate":"Discord.Audio.OpusEncodeStream.yml","Discord.Audio.OpusEncodeStream.Channels":"Discord.Audio.OpusEncodeStream.yml","Discord.Audio.OpusEncodeStream.Write(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.OpusEncodeStream.yml","Discord.Audio.OpusEncodeStream.Dispose(System.Boolean)":"Discord.Audio.OpusEncodeStream.yml","Discord.Audio.RTPReadStream":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.CanRead":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.CanSeek":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.CanWrite":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Read(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Write(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Flush":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Length":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Position":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.SetLength(System.Int64)":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPReadStream.Seek(System.Int64,System.IO.SeekOrigin)":"Discord.Audio.RTPReadStream.yml","Discord.Audio.RTPWriteStream":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream._buffer":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.CanRead":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.CanSeek":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.CanWrite":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Write(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Flush":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Length":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Position":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Read(System.Byte[],System.Int32,System.Int32)":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.SetLength(System.Int64)":"Discord.Audio.RTPWriteStream.yml","Discord.Audio.RTPWriteStream.Seek(System.Int64,System.IO.SeekOrigin)":"Discord.Audio.RTPWriteStream.yml","Discord.Logging":"Discord.Logging.yml","Discord.Logging.ILogger":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.Level":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.LogAsync(Discord.LogSeverity,System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.LogAsync(Discord.LogSeverity,System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.LogAsync(Discord.LogSeverity,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.ErrorAsync(System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.ErrorAsync(System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.ErrorAsync(System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.WarningAsync(System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.WarningAsync(System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.WarningAsync(System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.InfoAsync(System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.InfoAsync(System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.InfoAsync(System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.VerboseAsync(System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.VerboseAsync(System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.VerboseAsync(System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.DebugAsync(System.String,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.DebugAsync(System.FormattableString,System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogger.DebugAsync(System.Exception)":"Discord.Logging.ILogger.yml","Discord.Logging.ILogManager":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.Level":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.LogAsync(Discord.LogSeverity,System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.LogAsync(Discord.LogSeverity,System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.LogAsync(Discord.LogSeverity,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.ErrorAsync(System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.ErrorAsync(System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.ErrorAsync(System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.WarningAsync(System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.WarningAsync(System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.WarningAsync(System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.InfoAsync(System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.InfoAsync(System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.InfoAsync(System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.VerboseAsync(System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.VerboseAsync(System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.VerboseAsync(System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.DebugAsync(System.String,System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.DebugAsync(System.String,System.FormattableString,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.DebugAsync(System.String,System.Exception)":"Discord.Logging.ILogManager.yml","Discord.Logging.ILogManager.CreateLogger(System.String)":"Discord.Logging.ILogManager.yml","Discord.Rest":"Discord.Rest.yml","Discord.Rest.DiscordRestClient":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Log":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.LoggedIn":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.LoggedOut":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.ApiClient":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.LoginState":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor(Discord.Rest.DiscordRestConfig)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.LoginAsync(Discord.TokenType,System.String,System.Boolean)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.ValidateTokenAsync(Discord.TokenType,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.LogoutAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLogoutAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetChannelAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetPrivateChannelsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildEmbedAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildSummariesAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetUserAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetUserAsync(System.String,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetCurrentUserAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.QueryUsersAsync(System.String,System.Int32)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Dispose":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#ConnectionState":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#LogManager":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#ConnectAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#DisconnectAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestConfig":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.UserAgent":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.RestClientProvider":"Discord.Rest.DiscordRestConfig.yml","Discord.Rpc":"Discord.Rpc.yml","Discord.Rpc.DiscordRpcClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectionState":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApiClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(Discord.Rpc.DiscordRpcConfig)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ValidateTokenAsync(Discord.TokenType,System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.DisconnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.AuthorizeAsync(System.String[],System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent[])":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent[])":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent[])":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent[])":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Connected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Disconnected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Ready":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageReceived":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcConfig":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.RpcAPIVersion":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeStart":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeEnd":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.#ctor(System.String,System.String)":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.ClientId":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.Origin":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.WebSocketProvider":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.RpcChannelEvent":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStart":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStop":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcGuildEvent":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcGuildEvent.GuildStatus":"Discord.Rpc.RpcGuildEvent.yml","Discord.WebSocket":"Discord.WebSocket.yml","Discord.WebSocket.DiscordSocketClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ShardId":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectionState":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Latency":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ApiClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor(Discord.DiscordSocketConfig)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLogoutAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectAsync(System.Boolean)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DisconnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetVoiceRegionAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuildAsync(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuildEmbedAsync(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuildSummariesAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuildsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetChannelAsync(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetPrivateChannelsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUserAsync(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUserAsync(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetCurrentUserAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadAllUsersAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(System.Collections.Generic.IEnumerable{Discord.IGuild})":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(Discord.IGuild[])":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetVoiceRegionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Connected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Disconnected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Ready":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LatencyUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelDestroyed":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageReceived":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.JoinedGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LeftGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildAvailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUnavailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMembersDownloaded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserJoined":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserLeft":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserBanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUnbanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserPresenceUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserVoiceStateUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserIsTyping":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.Extensions":"Discord.WebSocket.Extensions.yml","Discord.WebSocket.Extensions.ChannelExtensions":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUser(Discord.IDMChannel,System.UInt64)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUsers(Discord.IDMChannel)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUser(Discord.IGroupChannel,System.UInt64)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUsers(Discord.IGroupChannel)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUser(Discord.ITextChannel,System.UInt64)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUsers(Discord.ITextChannel)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUser(Discord.IVoiceChannel,System.UInt64)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.ChannelExtensions.GetUsers(Discord.IVoiceChannel)":"Discord.WebSocket.Extensions.ChannelExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetChannel(Discord.IGuild,System.UInt64)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetChannels(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetTextChannel(Discord.IGuild,System.UInt64)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetTextChannels(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetVoiceChannel(Discord.IGuild,System.UInt64)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetVoiceChannels(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetCurrentUser(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetUser(Discord.IGuild,System.UInt64)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetUsers(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetUserCount(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml","Discord.WebSocket.Extensions.GuildExtensions.GetCachedUserCount(Discord.IGuild)":"Discord.WebSocket.Extensions.GuildExtensions.yml"} \ No newline at end of file +{"Discord":"Discord.yml","Discord.ConnectionState":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnected":"Discord.ConnectionState.yml","Discord.ConnectionState.Connecting":"Discord.ConnectionState.yml","Discord.ConnectionState.Connected":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnecting":"Discord.ConnectionState.yml","Discord.DiscordConfig":"Discord.DiscordConfig.yml","Discord.DiscordConfig.APIVersion":"Discord.DiscordConfig.yml","Discord.DiscordConfig.Version":"Discord.DiscordConfig.yml","Discord.DiscordConfig.ClientAPIUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.CDNUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.InviteUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.DefaultRequestTimeout":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessageSize":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessagesPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxUsersPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.LogLevel":"Discord.DiscordConfig.yml","Discord.Format":"Discord.Format.yml","Discord.Format.Bold(System.String)":"Discord.Format.yml","Discord.Format.Italics(System.String)":"Discord.Format.yml","Discord.Format.Underline(System.String)":"Discord.Format.yml","Discord.Format.Strikethrough(System.String)":"Discord.Format.yml","Discord.Format.Code(System.String,System.String)":"Discord.Format.yml","Discord.Format.Sanitize(System.String)":"Discord.Format.yml","Discord.IDiscordClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectionState":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ApiClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CurrentUser":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.DisconnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetApplicationInfoAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetConnectionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetInviteAsync(System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.String,System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionAsync(System.String)":"Discord.IDiscordClient.yml","Discord.LoginState":"Discord.LoginState.yml","Discord.LoginState.LoggedOut":"Discord.LoginState.yml","Discord.LoginState.LoggingIn":"Discord.LoginState.yml","Discord.LoginState.LoggedIn":"Discord.LoginState.yml","Discord.LoginState.LoggingOut":"Discord.LoginState.yml","Discord.RequestOptions":"Discord.RequestOptions.yml","Discord.RequestOptions.Default":"Discord.RequestOptions.yml","Discord.RequestOptions.Timeout":"Discord.RequestOptions.yml","Discord.RequestOptions.HeaderOnly":"Discord.RequestOptions.yml","Discord.RequestOptions.#ctor":"Discord.RequestOptions.yml","Discord.RequestOptions.Clone":"Discord.RequestOptions.yml","Discord.TokenType":"Discord.TokenType.yml","Discord.TokenType.User":"Discord.TokenType.yml","Discord.TokenType.Bearer":"Discord.TokenType.yml","Discord.TokenType.Bot":"Discord.TokenType.yml","Discord.CacheMode":"Discord.CacheMode.yml","Discord.CacheMode.AllowDownload":"Discord.CacheMode.yml","Discord.CacheMode.CacheOnly":"Discord.CacheMode.yml","Discord.IApplication":"Discord.IApplication.yml","Discord.IApplication.Name":"Discord.IApplication.yml","Discord.IApplication.Description":"Discord.IApplication.yml","Discord.IApplication.RPCOrigins":"Discord.IApplication.yml","Discord.IApplication.Flags":"Discord.IApplication.yml","Discord.IApplication.IconUrl":"Discord.IApplication.yml","Discord.IApplication.Owner":"Discord.IApplication.yml","Discord.IDeletable":"Discord.IDeletable.yml","Discord.IDeletable.DeleteAsync(Discord.RequestOptions)":"Discord.IDeletable.yml","Discord.IEntity`1":"Discord.IEntity-1.yml","Discord.IEntity`1.Id":"Discord.IEntity-1.yml","Discord.IMentionable":"Discord.IMentionable.yml","Discord.IMentionable.Mention":"Discord.IMentionable.yml","Discord.ISnowflakeEntity":"Discord.ISnowflakeEntity.yml","Discord.ISnowflakeEntity.CreatedAt":"Discord.ISnowflakeEntity.yml","Discord.IUpdateable":"Discord.IUpdateable.yml","Discord.IUpdateable.UpdateAsync(Discord.RequestOptions)":"Discord.IUpdateable.yml","Discord.ChannelType":"Discord.ChannelType.yml","Discord.ChannelType.Text":"Discord.ChannelType.yml","Discord.ChannelType.DM":"Discord.ChannelType.yml","Discord.ChannelType.Voice":"Discord.ChannelType.yml","Discord.ChannelType.Group":"Discord.ChannelType.yml","Discord.IAudioChannel":"Discord.IAudioChannel.yml","Discord.IChannel":"Discord.IChannel.yml","Discord.IChannel.Name":"Discord.IChannel.yml","Discord.IChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IDMChannel":"Discord.IDMChannel.yml","Discord.IDMChannel.Recipient":"Discord.IDMChannel.yml","Discord.IDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.IDMChannel.yml","Discord.IGroupChannel":"Discord.IGroupChannel.yml","Discord.IGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.IGroupChannel.yml","Discord.IGuildChannel":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Position":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Guild":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GuildId":"Discord.IGuildChannel.yml","Discord.IGuildChannel.PermissionOverwrites":"Discord.IGuildChannel.yml","Discord.IGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IMessageChannel":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.EnterTypingState(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IPrivateChannel":"Discord.IPrivateChannel.yml","Discord.IPrivateChannel.Recipients":"Discord.IPrivateChannel.yml","Discord.ITextChannel":"Discord.ITextChannel.yml","Discord.ITextChannel.Topic":"Discord.ITextChannel.yml","Discord.ITextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.ITextChannel.yml","Discord.IVoiceChannel":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.Bitrate":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.UserLimit":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ConnectAsync":"Discord.IVoiceChannel.yml","Discord.DefaultMessageNotifications":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.AllMessages":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.MentionsOnly":"Discord.DefaultMessageNotifications.yml","Discord.GuildEmoji":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Id":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Name":"Discord.GuildEmoji.yml","Discord.GuildEmoji.IsManaged":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RequireColons":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RoleIds":"Discord.GuildEmoji.yml","Discord.GuildEmoji.ToString":"Discord.GuildEmoji.yml","Discord.IBan":"Discord.IBan.yml","Discord.IBan.User":"Discord.IBan.yml","Discord.IBan.Reason":"Discord.IBan.yml","Discord.IGuild":"Discord.IGuild.yml","Discord.IGuild.Name":"Discord.IGuild.yml","Discord.IGuild.AFKTimeout":"Discord.IGuild.yml","Discord.IGuild.IsEmbeddable":"Discord.IGuild.yml","Discord.IGuild.DefaultMessageNotifications":"Discord.IGuild.yml","Discord.IGuild.MfaLevel":"Discord.IGuild.yml","Discord.IGuild.VerificationLevel":"Discord.IGuild.yml","Discord.IGuild.IconId":"Discord.IGuild.yml","Discord.IGuild.IconUrl":"Discord.IGuild.yml","Discord.IGuild.SplashId":"Discord.IGuild.yml","Discord.IGuild.SplashUrl":"Discord.IGuild.yml","Discord.IGuild.Available":"Discord.IGuild.yml","Discord.IGuild.AFKChannelId":"Discord.IGuild.yml","Discord.IGuild.DefaultChannelId":"Discord.IGuild.yml","Discord.IGuild.EmbedChannelId":"Discord.IGuild.yml","Discord.IGuild.OwnerId":"Discord.IGuild.yml","Discord.IGuild.VoiceRegionId":"Discord.IGuild.yml","Discord.IGuild.AudioClient":"Discord.IGuild.yml","Discord.IGuild.EveryoneRole":"Discord.IGuild.yml","Discord.IGuild.Emojis":"Discord.IGuild.yml","Discord.IGuild.Features":"Discord.IGuild.yml","Discord.IGuild.Roles":"Discord.IGuild.yml","Discord.IGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.LeaveAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetBansAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetRole(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.DownloadUsersAsync":"Discord.IGuild.yml","Discord.IGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuildIntegration":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Id":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Name":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Type":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsEnabled":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsSyncing":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireBehavior":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireGracePeriod":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.SyncedAt":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Account":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Guild":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.GuildId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.RoleId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.User":"Discord.IGuildIntegration.yml","Discord.IntegrationAccount":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Id":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Name":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.ToString":"Discord.IntegrationAccount.yml","Discord.IUserGuild":"Discord.IUserGuild.yml","Discord.IUserGuild.Name":"Discord.IUserGuild.yml","Discord.IUserGuild.IconUrl":"Discord.IUserGuild.yml","Discord.IUserGuild.IsOwner":"Discord.IUserGuild.yml","Discord.IUserGuild.Permissions":"Discord.IUserGuild.yml","Discord.IVoiceRegion":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Id":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Name":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsVip":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsOptimal":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SampleHostname":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SamplePort":"Discord.IVoiceRegion.yml","Discord.MfaLevel":"Discord.MfaLevel.yml","Discord.MfaLevel.Disabled":"Discord.MfaLevel.yml","Discord.MfaLevel.Enabled":"Discord.MfaLevel.yml","Discord.VerificationLevel":"Discord.VerificationLevel.yml","Discord.VerificationLevel.None":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Low":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Medium":"Discord.VerificationLevel.yml","Discord.VerificationLevel.High":"Discord.VerificationLevel.yml","Discord.IInvite":"Discord.IInvite.yml","Discord.IInvite.Code":"Discord.IInvite.yml","Discord.IInvite.Url":"Discord.IInvite.yml","Discord.IInvite.Channel":"Discord.IInvite.yml","Discord.IInvite.ChannelId":"Discord.IInvite.yml","Discord.IInvite.Guild":"Discord.IInvite.yml","Discord.IInvite.GuildId":"Discord.IInvite.yml","Discord.IInvite.AcceptAsync(Discord.RequestOptions)":"Discord.IInvite.yml","Discord.IInviteMetadata":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Inviter":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsRevoked":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsTemporary":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxAge":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxUses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Uses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.CreatedAt":"Discord.IInviteMetadata.yml","Discord.Direction":"Discord.Direction.yml","Discord.Direction.Before":"Discord.Direction.yml","Discord.Direction.After":"Discord.Direction.yml","Discord.Direction.Around":"Discord.Direction.yml","Discord.EmbedAuthor":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Name":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Url":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.IconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ProxyIconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ToString":"Discord.EmbedAuthor.yml","Discord.EmbedBuilder":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.#ctor":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Title":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Description":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Url":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ThumbnailUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ImageUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Timestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Color":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Author":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Footer":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTitle(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithDescription(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithThumbnailUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithImageUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithCurrentTimestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTimestamp(System.DateTimeOffset)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithColor(Discord.Color)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(Discord.EmbedAuthorBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(System.Action{Discord.EmbedAuthorBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(Discord.EmbedFooterBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(System.Action{Discord.EmbedFooterBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.AddField(System.Action{Discord.EmbedFieldBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedFieldBuilder":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Name":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Value":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.IsInline":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.#ctor":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithName(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithValue(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithIsInline(System.Boolean)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedAuthorBuilder":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Name":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Url":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.IconUrl":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.#ctor":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithName(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithIconUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedFooterBuilder":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.Text":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.IconUrl":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.#ctor":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithText(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithIconUrl(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedField":"Discord.EmbedField.yml","Discord.EmbedField.Name":"Discord.EmbedField.yml","Discord.EmbedField.Value":"Discord.EmbedField.yml","Discord.EmbedField.Inline":"Discord.EmbedField.yml","Discord.EmbedField.ToString":"Discord.EmbedField.yml","Discord.EmbedFooter":"Discord.EmbedFooter.yml","Discord.EmbedFooter.Text":"Discord.EmbedFooter.yml","Discord.EmbedFooter.IconUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ProxyUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ToString":"Discord.EmbedFooter.yml","Discord.EmbedImage":"Discord.EmbedImage.yml","Discord.EmbedImage.Url":"Discord.EmbedImage.yml","Discord.EmbedImage.ProxyUrl":"Discord.EmbedImage.yml","Discord.EmbedImage.Height":"Discord.EmbedImage.yml","Discord.EmbedImage.Width":"Discord.EmbedImage.yml","Discord.EmbedImage.ToString":"Discord.EmbedImage.yml","Discord.EmbedProvider":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Name":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Url":"Discord.EmbedProvider.yml","Discord.EmbedProvider.ToString":"Discord.EmbedProvider.yml","Discord.EmbedThumbnail":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Url":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ProxyUrl":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Height":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Width":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ToString":"Discord.EmbedThumbnail.yml","Discord.EmbedVideo":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Url":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Height":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Width":"Discord.EmbedVideo.yml","Discord.EmbedVideo.ToString":"Discord.EmbedVideo.yml","Discord.Emoji":"Discord.Emoji.yml","Discord.Emoji.Id":"Discord.Emoji.yml","Discord.Emoji.Name":"Discord.Emoji.yml","Discord.Emoji.Url":"Discord.Emoji.yml","Discord.Emoji.Parse(System.String)":"Discord.Emoji.yml","Discord.Emoji.TryParse(System.String,Discord.Emoji@)":"Discord.Emoji.yml","Discord.Emoji.ToString":"Discord.Emoji.yml","Discord.IAttachment":"Discord.IAttachment.yml","Discord.IAttachment.Id":"Discord.IAttachment.yml","Discord.IAttachment.Filename":"Discord.IAttachment.yml","Discord.IAttachment.Url":"Discord.IAttachment.yml","Discord.IAttachment.ProxyUrl":"Discord.IAttachment.yml","Discord.IAttachment.Size":"Discord.IAttachment.yml","Discord.IAttachment.Height":"Discord.IAttachment.yml","Discord.IAttachment.Width":"Discord.IAttachment.yml","Discord.IEmbed":"Discord.IEmbed.yml","Discord.IEmbed.Url":"Discord.IEmbed.yml","Discord.IEmbed.Type":"Discord.IEmbed.yml","Discord.IEmbed.Title":"Discord.IEmbed.yml","Discord.IEmbed.Description":"Discord.IEmbed.yml","Discord.IEmbed.Timestamp":"Discord.IEmbed.yml","Discord.IEmbed.Color":"Discord.IEmbed.yml","Discord.IEmbed.Image":"Discord.IEmbed.yml","Discord.IEmbed.Video":"Discord.IEmbed.yml","Discord.IEmbed.Author":"Discord.IEmbed.yml","Discord.IEmbed.Footer":"Discord.IEmbed.yml","Discord.IEmbed.Provider":"Discord.IEmbed.yml","Discord.IEmbed.Thumbnail":"Discord.IEmbed.yml","Discord.IEmbed.Fields":"Discord.IEmbed.yml","Discord.IMessage":"Discord.IMessage.yml","Discord.IMessage.Type":"Discord.IMessage.yml","Discord.IMessage.IsTTS":"Discord.IMessage.yml","Discord.IMessage.IsPinned":"Discord.IMessage.yml","Discord.IMessage.IsWebhook":"Discord.IMessage.yml","Discord.IMessage.Content":"Discord.IMessage.yml","Discord.IMessage.Timestamp":"Discord.IMessage.yml","Discord.IMessage.EditedTimestamp":"Discord.IMessage.yml","Discord.IMessage.Channel":"Discord.IMessage.yml","Discord.IMessage.Author":"Discord.IMessage.yml","Discord.IMessage.WebhookId":"Discord.IMessage.yml","Discord.IMessage.Attachments":"Discord.IMessage.yml","Discord.IMessage.Embeds":"Discord.IMessage.yml","Discord.IMessage.Tags":"Discord.IMessage.yml","Discord.IMessage.MentionedChannelIds":"Discord.IMessage.yml","Discord.IMessage.MentionedRoleIds":"Discord.IMessage.yml","Discord.IMessage.MentionedUserIds":"Discord.IMessage.yml","Discord.IReaction":"Discord.IReaction.yml","Discord.IReaction.Emoji":"Discord.IReaction.yml","Discord.ISystemMessage":"Discord.ISystemMessage.yml","Discord.ITag":"Discord.ITag.yml","Discord.ITag.Index":"Discord.ITag.yml","Discord.ITag.Length":"Discord.ITag.yml","Discord.ITag.Type":"Discord.ITag.yml","Discord.ITag.Key":"Discord.ITag.yml","Discord.ITag.Value":"Discord.ITag.yml","Discord.IUserMessage":"Discord.IUserMessage.yml","Discord.IUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.PinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Reactions":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.IUserMessage.yml","Discord.MessageType":"Discord.MessageType.yml","Discord.MessageType.Default":"Discord.MessageType.yml","Discord.MessageType.RecipientAdd":"Discord.MessageType.yml","Discord.MessageType.RecipientRemove":"Discord.MessageType.yml","Discord.MessageType.Call":"Discord.MessageType.yml","Discord.MessageType.ChannelNameChange":"Discord.MessageType.yml","Discord.MessageType.ChannelIconChange":"Discord.MessageType.yml","Discord.MessageType.ChannelPinnedMessage":"Discord.MessageType.yml","Discord.ModifyMessageParams":"Discord.ModifyMessageParams.yml","Discord.ModifyMessageParams.Content":"Discord.ModifyMessageParams.yml","Discord.ModifyMessageParams.Embed":"Discord.ModifyMessageParams.yml","Discord.Tag`1":"Discord.Tag-1.yml","Discord.Tag`1.Type":"Discord.Tag-1.yml","Discord.Tag`1.Index":"Discord.Tag-1.yml","Discord.Tag`1.Length":"Discord.Tag-1.yml","Discord.Tag`1.Key":"Discord.Tag-1.yml","Discord.Tag`1.Value":"Discord.Tag-1.yml","Discord.Tag`1.ToString":"Discord.Tag-1.yml","Discord.Tag`1.Discord#ITag#Value":"Discord.Tag-1.yml","Discord.TagHandling":"Discord.TagHandling.yml","Discord.TagHandling.Ignore":"Discord.TagHandling.yml","Discord.TagHandling.Remove":"Discord.TagHandling.yml","Discord.TagHandling.Name":"Discord.TagHandling.yml","Discord.TagHandling.NameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.FullName":"Discord.TagHandling.yml","Discord.TagHandling.FullNameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.Sanitize":"Discord.TagHandling.yml","Discord.TagType":"Discord.TagType.yml","Discord.TagType.UserMention":"Discord.TagType.yml","Discord.TagType.ChannelMention":"Discord.TagType.yml","Discord.TagType.RoleMention":"Discord.TagType.yml","Discord.TagType.EveryoneMention":"Discord.TagType.yml","Discord.TagType.HereMention":"Discord.TagType.yml","Discord.TagType.Emoji":"Discord.TagType.yml","Discord.ChannelPermission":"Discord.ChannelPermission.yml","Discord.ChannelPermission.CreateInstantInvite":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageChannel":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AddReactions":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendTTSMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.EmbedLinks":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AttachFiles":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessageHistory":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MentionEveryone":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseExternalEmojis":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Connect":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Speak":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MuteMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.DeafenMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MoveMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseVAD":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManagePermissions":"Discord.ChannelPermission.yml","Discord.ChannelPermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.None":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.All(Discord.IChannel)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.RawValue":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.CreateInstantInvite":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageChannel":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AddReactions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendTTSMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.EmbedLinks":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AttachFiles":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessageHistory":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MentionEveryone":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseExternalEmojis":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Connect":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Speak":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MuteMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.DeafenMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MoveMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseVAD":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManagePermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.UInt64)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Has(Discord.ChannelPermission)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToList":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToString":"Discord.ChannelPermissions.yml","Discord.GuildPermission":"Discord.GuildPermission.yml","Discord.GuildPermission.CreateInstantInvite":"Discord.GuildPermission.yml","Discord.GuildPermission.KickMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.BanMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.Administrator":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageChannels":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageGuild":"Discord.GuildPermission.yml","Discord.GuildPermission.AddReactions":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendTTSMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.EmbedLinks":"Discord.GuildPermission.yml","Discord.GuildPermission.AttachFiles":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessageHistory":"Discord.GuildPermission.yml","Discord.GuildPermission.MentionEveryone":"Discord.GuildPermission.yml","Discord.GuildPermission.UseExternalEmojis":"Discord.GuildPermission.yml","Discord.GuildPermission.Connect":"Discord.GuildPermission.yml","Discord.GuildPermission.Speak":"Discord.GuildPermission.yml","Discord.GuildPermission.MuteMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.DeafenMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.MoveMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.UseVAD":"Discord.GuildPermission.yml","Discord.GuildPermission.ChangeNickname":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageNicknames":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageRoles":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageWebhooks":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageEmojis":"Discord.GuildPermission.yml","Discord.GuildPermissions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.None":"Discord.GuildPermissions.yml","Discord.GuildPermissions.All":"Discord.GuildPermissions.yml","Discord.GuildPermissions.RawValue":"Discord.GuildPermissions.yml","Discord.GuildPermissions.CreateInstantInvite":"Discord.GuildPermissions.yml","Discord.GuildPermissions.BanMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.KickMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Administrator":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageChannels":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageGuild":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AddReactions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendTTSMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.EmbedLinks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AttachFiles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessageHistory":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MentionEveryone":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseExternalEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Connect":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Speak":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MuteMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.DeafenMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MoveMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseVAD":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ChangeNickname":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageNicknames":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageRoles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageWebhooks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.UInt64)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Boolean,System.Boolean)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Has(Discord.GuildPermission)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToList":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToString":"Discord.GuildPermissions.yml","Discord.Overwrite":"Discord.Overwrite.yml","Discord.Overwrite.TargetId":"Discord.Overwrite.yml","Discord.Overwrite.TargetType":"Discord.Overwrite.yml","Discord.Overwrite.Permissions":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(System.UInt64,Discord.PermissionTarget,Discord.OverwritePermissions)":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(Discord.API.Overwrite)":"Discord.Overwrite.yml","Discord.OverwritePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.InheritAll":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.CreateInstantInvite":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageChannel":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AddReactions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendTTSMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.EmbedLinks":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AttachFiles":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessageHistory":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MentionEveryone":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseExternalEmojis":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Connect":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Speak":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MuteMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DeafenMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MoveMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseVAD":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManagePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(System.UInt64,System.UInt64)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Modify(System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue})":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToAllowList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToDenyList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToString":"Discord.OverwritePermissions.yml","Discord.PermissionTarget":"Discord.PermissionTarget.yml","Discord.PermissionTarget.Role":"Discord.PermissionTarget.yml","Discord.PermissionTarget.User":"Discord.PermissionTarget.yml","Discord.PermValue":"Discord.PermValue.yml","Discord.PermValue.Allow":"Discord.PermValue.yml","Discord.PermValue.Deny":"Discord.PermValue.yml","Discord.PermValue.Inherit":"Discord.PermValue.yml","Discord.Color":"Discord.Color.yml","Discord.Color.Default":"Discord.Color.yml","Discord.Color.RawValue":"Discord.Color.yml","Discord.Color.R":"Discord.Color.yml","Discord.Color.G":"Discord.Color.yml","Discord.Color.B":"Discord.Color.yml","Discord.Color.#ctor(System.UInt32)":"Discord.Color.yml","Discord.Color.#ctor(System.Byte,System.Byte,System.Byte)":"Discord.Color.yml","Discord.Color.#ctor(System.Single,System.Single,System.Single)":"Discord.Color.yml","Discord.Color.ToString":"Discord.Color.yml","Discord.IRole":"Discord.IRole.yml","Discord.IRole.Guild":"Discord.IRole.yml","Discord.IRole.Color":"Discord.IRole.yml","Discord.IRole.IsHoisted":"Discord.IRole.yml","Discord.IRole.IsManaged":"Discord.IRole.yml","Discord.IRole.IsMentionable":"Discord.IRole.yml","Discord.IRole.Name":"Discord.IRole.yml","Discord.IRole.Permissions":"Discord.IRole.yml","Discord.IRole.Position":"Discord.IRole.yml","Discord.IRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.IRole.yml","Discord.Game":"Discord.Game.yml","Discord.Game.Name":"Discord.Game.yml","Discord.Game.StreamUrl":"Discord.Game.yml","Discord.Game.StreamType":"Discord.Game.yml","Discord.Game.#ctor(System.String,System.String,Discord.StreamType)":"Discord.Game.yml","Discord.Game.ToString":"Discord.Game.yml","Discord.IConnection":"Discord.IConnection.yml","Discord.IConnection.Id":"Discord.IConnection.yml","Discord.IConnection.Type":"Discord.IConnection.yml","Discord.IConnection.Name":"Discord.IConnection.yml","Discord.IConnection.IsRevoked":"Discord.IConnection.yml","Discord.IConnection.IntegrationIds":"Discord.IConnection.yml","Discord.IGroupUser":"Discord.IGroupUser.yml","Discord.IGuildUser":"Discord.IGuildUser.yml","Discord.IGuildUser.JoinedAt":"Discord.IGuildUser.yml","Discord.IGuildUser.Nickname":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildPermissions":"Discord.IGuildUser.yml","Discord.IGuildUser.Guild":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildId":"Discord.IGuildUser.yml","Discord.IGuildUser.RoleIds":"Discord.IGuildUser.yml","Discord.IGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.IGuildUser.yml","Discord.IGuildUser.KickAsync(Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IPresence":"Discord.IPresence.yml","Discord.IPresence.Game":"Discord.IPresence.yml","Discord.IPresence.Status":"Discord.IPresence.yml","Discord.ISelfUser":"Discord.ISelfUser.yml","Discord.ISelfUser.Email":"Discord.ISelfUser.yml","Discord.ISelfUser.IsVerified":"Discord.ISelfUser.yml","Discord.ISelfUser.IsMfaEnabled":"Discord.ISelfUser.yml","Discord.ISelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.ISelfUser.yml","Discord.IUser":"Discord.IUser.yml","Discord.IUser.AvatarId":"Discord.IUser.yml","Discord.IUser.AvatarUrl":"Discord.IUser.yml","Discord.IUser.Discriminator":"Discord.IUser.yml","Discord.IUser.DiscriminatorValue":"Discord.IUser.yml","Discord.IUser.IsBot":"Discord.IUser.yml","Discord.IUser.Username":"Discord.IUser.yml","Discord.IUser.GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IUser.yml","Discord.IUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.IUser.yml","Discord.IVoiceState":"Discord.IVoiceState.yml","Discord.IVoiceState.IsDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSuppressed":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceChannel":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceSessionId":"Discord.IVoiceState.yml","Discord.StreamType":"Discord.StreamType.yml","Discord.StreamType.NotStreaming":"Discord.StreamType.yml","Discord.StreamType.Twitch":"Discord.StreamType.yml","Discord.UserStatus":"Discord.UserStatus.yml","Discord.UserStatus.Unknown":"Discord.UserStatus.yml","Discord.UserStatus.Online":"Discord.UserStatus.yml","Discord.UserStatus.Idle":"Discord.UserStatus.yml","Discord.UserStatus.AFK":"Discord.UserStatus.yml","Discord.UserStatus.DoNotDisturb":"Discord.UserStatus.yml","Discord.UserStatus.Invisible":"Discord.UserStatus.yml","Discord.UserStatus.Offline":"Discord.UserStatus.yml","Discord.AsyncEnumerableExtensions":"Discord.AsyncEnumerableExtensions.yml","Discord.AsyncEnumerableExtensions.Flatten``1(System.Collections.Generic.IAsyncEnumerable{System.Collections.Generic.IReadOnlyCollection{``0}})":"Discord.AsyncEnumerableExtensions.yml","Discord.DiscordClientExtensions":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetPrivateChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetOptimalVoiceRegionAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.GuildExtensions":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetAFKChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetDefaultChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetEmbedChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetOwnerAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildUserExtensions":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.ChangeRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole},System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.LogMessage":"Discord.LogMessage.yml","Discord.LogMessage.Severity":"Discord.LogMessage.yml","Discord.LogMessage.Source":"Discord.LogMessage.yml","Discord.LogMessage.Message":"Discord.LogMessage.yml","Discord.LogMessage.Exception":"Discord.LogMessage.yml","Discord.LogMessage.#ctor(Discord.LogSeverity,System.String,System.String,System.Exception)":"Discord.LogMessage.yml","Discord.LogMessage.ToString":"Discord.LogMessage.yml","Discord.LogMessage.ToString(System.Text.StringBuilder,System.Boolean,System.Boolean,System.DateTimeKind,System.Nullable{System.Int32})":"Discord.LogMessage.yml","Discord.LogSeverity":"Discord.LogSeverity.yml","Discord.LogSeverity.Critical":"Discord.LogSeverity.yml","Discord.LogSeverity.Error":"Discord.LogSeverity.yml","Discord.LogSeverity.Warning":"Discord.LogSeverity.yml","Discord.LogSeverity.Info":"Discord.LogSeverity.yml","Discord.LogSeverity.Verbose":"Discord.LogSeverity.yml","Discord.LogSeverity.Debug":"Discord.LogSeverity.yml","Discord.RpcException":"Discord.RpcException.yml","Discord.RpcException.ErrorCode":"Discord.RpcException.yml","Discord.RpcException.Reason":"Discord.RpcException.yml","Discord.RpcException.#ctor(System.Int32,System.String)":"Discord.RpcException.yml","Discord.MentionUtils":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionUser(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionChannel(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionRole(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseUser(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseUser(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseChannel(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseChannel(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseRole(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseRole(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.Optional`1":"Discord.Optional-1.yml","Discord.Optional`1.Unspecified":"Discord.Optional-1.yml","Discord.Optional`1.Value":"Discord.Optional-1.yml","Discord.Optional`1.IsSpecified":"Discord.Optional-1.yml","Discord.Optional`1.#ctor(`0)":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault(`0)":"Discord.Optional-1.yml","Discord.Optional`1.Equals(System.Object)":"Discord.Optional-1.yml","Discord.Optional`1.GetHashCode":"Discord.Optional-1.yml","Discord.Optional`1.ToString":"Discord.Optional-1.yml","Discord.Optional`1.op_Implicit(`0)~Discord.Optional{`0}":"Discord.Optional-1.yml","Discord.Optional`1.op_Explicit(Discord.Optional{`0})~`0":"Discord.Optional-1.yml","Discord.Optional":"Discord.Optional.yml","Discord.Optional.Create``1":"Discord.Optional.yml","Discord.Optional.Create``1(``0)":"Discord.Optional.yml","Discord.RestGuildEmbed":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.IsEnabled":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ChannelId":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ToString":"Discord.RestGuildEmbed.yml","Discord.RestVoiceRegion":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.Name":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsVip":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsOptimal":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SampleHostname":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SamplePort":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.ToString":"Discord.RestVoiceRegion.yml","Discord.Attachment":"Discord.Attachment.yml","Discord.Attachment.Id":"Discord.Attachment.yml","Discord.Attachment.Filename":"Discord.Attachment.yml","Discord.Attachment.Url":"Discord.Attachment.yml","Discord.Attachment.ProxyUrl":"Discord.Attachment.yml","Discord.Attachment.Size":"Discord.Attachment.yml","Discord.Attachment.Height":"Discord.Attachment.yml","Discord.Attachment.Width":"Discord.Attachment.yml","Discord.Attachment.ToString":"Discord.Attachment.yml","Discord.Embed":"Discord.Embed.yml","Discord.Embed.Description":"Discord.Embed.yml","Discord.Embed.Url":"Discord.Embed.yml","Discord.Embed.Title":"Discord.Embed.yml","Discord.Embed.Type":"Discord.Embed.yml","Discord.Embed.Timestamp":"Discord.Embed.yml","Discord.Embed.Color":"Discord.Embed.yml","Discord.Embed.Image":"Discord.Embed.yml","Discord.Embed.Video":"Discord.Embed.yml","Discord.Embed.Author":"Discord.Embed.yml","Discord.Embed.Footer":"Discord.Embed.yml","Discord.Embed.Provider":"Discord.Embed.yml","Discord.Embed.Thumbnail":"Discord.Embed.yml","Discord.Embed.Fields":"Discord.Embed.yml","Discord.Embed.ToString":"Discord.Embed.yml","Discord.RestReaction":"Discord.RestReaction.yml","Discord.RestReaction.Emoji":"Discord.RestReaction.yml","Discord.RestReaction.Count":"Discord.RestReaction.yml","Discord.RestReaction.Me":"Discord.RestReaction.yml","Discord.RestConnection":"Discord.RestConnection.yml","Discord.RestConnection.Id":"Discord.RestConnection.yml","Discord.RestConnection.Type":"Discord.RestConnection.yml","Discord.RestConnection.Name":"Discord.RestConnection.yml","Discord.RestConnection.IsRevoked":"Discord.RestConnection.yml","Discord.RestConnection.IntegrationIds":"Discord.RestConnection.yml","Discord.RestConnection.ToString":"Discord.RestConnection.yml","Discord.Audio":"Discord.Audio.yml","Discord.Audio.IAudioClient":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Connected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Disconnected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.LatencyUpdated":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.ConnectionState":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Latency":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.DisconnectAsync":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreatePCMStream(System.Int32,System.Nullable{System.Int32},Discord.Audio.OpusApplication,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.OpusApplication":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.Voice":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.MusicOrMixed":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.LowLatency":"Discord.Audio.OpusApplication.yml","Discord.Audio.DiscordVoiceAPIClient":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.MaxBitrate":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Mode":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentRequest":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentGatewayMessage":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentDiscovery":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentData":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedEvent":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedPacket":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Disconnected":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.GuildId":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectionState":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Dispose":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(Discord.API.Voice.VoiceOpCode,System.Object,Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(System.Byte[],System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendHeartbeatAsync(Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendIdentityAsync(System.UInt64,System.String,System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSelectProtocol(System.String,System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSetSpeaking(System.Boolean)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectAsync(System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.DisconnectAsync":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendDiscoveryAsync(System.UInt32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SetUdpEndpoint(System.Net.IPEndPoint)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.AudioClient":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Connected":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Disconnected":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.LatencyUpdated":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Guild":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.ApiClient":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.ConnectionState":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Latency":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.DisconnectAsync":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Send(System.Byte[],System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.CreatePCMStream(System.Int32,System.Nullable{System.Int32},Discord.Audio.OpusApplication,System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Dispose":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioMode":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Disabled":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Outgoing":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Incoming":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Both":"Discord.Audio.AudioMode.yml","Discord.Audio.SecretBox":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Encrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Decrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Rest":"Discord.Rest.yml","Discord.Rest.BaseDiscordClient":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Log":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedIn":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedOut":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.ApiClient":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginState":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.CurrentUser":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginAsync(Discord.TokenType,System.String,System.Boolean)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Dispose":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#ConnectionState":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#CurrentUser":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetUserAsync(System.String,System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#ConnectAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#DisconnectAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.DiscordRestClient":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CurrentUser":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor(Discord.Rest.DiscordRestConfig)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLogoutAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetChannelAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetPrivateChannelsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildEmbedAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildSummariesAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetUserAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildUserAsync(System.UInt64,System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestConfig":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.UserAgent":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.RestClientProvider":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.RestApplication":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication._iconId":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Name":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Description":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.RPCOrigins":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Flags":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Owner":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.CreatedAt":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.IconUrl":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.UpdateAsync":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.ToString":"Discord.Rest.RestApplication.yml","Discord.Rest.RestEntity`1":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Discord":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Id":"Discord.Rest.RestEntity-1.yml","Discord.Rest.IRestAudioChannel":"Discord.Rest.IRestAudioChannel.yml","Discord.Rest.IRestMessageChannel":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestPrivateChannel":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.IRestPrivateChannel.Recipients":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.RestChannel":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.CreatedAt":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#Name":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestDMChannel":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CurrentUser":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Recipient":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Users":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetUser(System.UInt64)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.ToString":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IDMChannel#Recipient":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IPrivateChannel#Recipients":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#Name":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestGroupChannel":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Name":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Users":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetUser(System.UInt64)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.ToString":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGuildChannel":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.PermissionOverwrites":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Name":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Position":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GuildId":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ToString":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#Guild":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestTextChannel":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Topic":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Mention":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUserAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUsersAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestVoiceChannel":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Bitrate":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.UserLimit":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestBan":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.User":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.Reason":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.ToString":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.Discord#IBan#User":"Discord.Rest.RestBan.yml","Discord.Rest.RestGuild":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Name":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKTimeout":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IsEmbeddable":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VerificationLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.MfaLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultMessageNotifications":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EmbedChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.OwnerId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VoiceRegionId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreatedAt":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EveryoneRole":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Roles":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Emojis":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Features":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetBansAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetRole(System.UInt64)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUsersAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUserAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetCurrentUserAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ToString":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#Available":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#AudioClient":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#EveryoneRole":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#Roles":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetBansAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetIntegrationsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetRole(System.UInt64)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#DownloadUsersAsync":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuildIntegration":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Name":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Type":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsEnabled":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsSyncing":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireBehavior":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireGracePeriod":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.GuildId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.RoleId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.User":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Account":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncedAt":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Update(Discord.API.Integration)":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.DeleteAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildIntegrationParams})":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ToString":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Discord#IGuildIntegration#Guild":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Discord#IGuildIntegration#User":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestUserGuild":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Name":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IsOwner":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Permissions":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.CreatedAt":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IconUrl":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Update(Discord.API.UserGuild)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.ToString":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestInvite":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Code":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Url":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.AcceptAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ToString":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Discord#IInvite#Guild":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Discord#IInvite#Channel":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInviteMetadata":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsRevoked":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsTemporary":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxAge":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxUses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Uses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Inviter":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.CreatedAt":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Discord#IInviteMetadata#Inviter":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestMessage":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Channel":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Author":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Content":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.CreatedAt":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsTTS":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsPinned":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.EditedTimestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Attachments":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Embeds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedChannelIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedRoleIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedUsers":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Tags":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.WebhookId":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsWebhook":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Timestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.ToString":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Type":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Author":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Attachments":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Embeds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#MentionedUserIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestSystemMessage":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestSystemMessage.Type":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestUserMessage":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsTTS":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsPinned":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.WebhookId":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.EditedTimestamp":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Attachments":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Embeds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedChannelIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedRoleIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedUsers":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Tags":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Reactions":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.PinAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestRole":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Color":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsHoisted":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsManaged":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsMentionable":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Name":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Permissions":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Position":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CreatedAt":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsEveryone":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Mention":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CompareTo(Discord.IRole)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ToString":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Discord#IRole#Guild":"Discord.Rest.RestRole.yml","Discord.Rest.RestGroupUser":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsDeafened":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsMuted":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSelfDeafened":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSelfMuted":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSuppressed":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#VoiceChannel":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#VoiceSessionId":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGuildUser":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Nickname":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsDeafened":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsMuted":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildId":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildPermissions":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.RoleIds":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.JoinedAt":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.KickAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IGuildUser#Guild":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSelfDeafened":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSelfMuted":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSuppressed":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#VoiceChannel":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#VoiceSessionId":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestSelfUser":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.Email":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsVerified":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsMfaEnabled":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestUser":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.IsBot":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Username":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.DiscriminatorValue":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarId":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarUrl":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreatedAt":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discriminator":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Mention":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Game":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Status":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.ToString":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rpc":"Discord.Rpc.yml","Discord.Rpc.DiscordRpcClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectionState":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Scopes":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.TokenExpiresAt":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApiClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.CurrentUser":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApplicationInfo":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String,Discord.Rpc.DiscordRpcConfig)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.DisconnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.AuthorizeAsync(System.String[],System.String,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGlobal(Discord.Rpc.RpcGlobalEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGlobal(Discord.Rpc.RpcGlobalEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildsAsync(Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelsAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(Discord.IChannel,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(Discord.Rpc.RpcChannelSummary,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(Discord.IChannel,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(Discord.Rpc.RpcChannelSummary,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(System.UInt64,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetVoiceSettingsAsync(Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetVoiceSettingsAsync(System.Action{Discord.API.Rpc.VoiceSettings},Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetUserVoiceSettingsAsync(System.UInt64,System.Action{Discord.API.Rpc.UserVoiceSettings},Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Connected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Disconnected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Ready":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ChannelCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildStatusUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStarted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStopped":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceSettingsUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageReceived":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcConfig":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.RpcAPIVersion":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeStart":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeEnd":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.ConnectionTimeout":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.WebSocketProvider":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.RpcChannelEvent":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStart":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStop":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcGlobalEvent":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.ChannelCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.GuildCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.VoiceSettingsUpdated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGuildEvent":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcGuildEvent.GuildStatus":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcEntity`1":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Discord":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Id":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.VoiceDevice":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Id":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Name":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.ToString":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceSettings":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableInputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableOutputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutomaticGainControl":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.EchoCancellation":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.NoiseSuppression":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.QualityOfService":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.SilenceWarning":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.ActivationMode":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutoThreshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Threshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Shortcuts":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Delay":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceShortcut":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Type":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Code":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Name":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.ToString":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcutType":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.MouseButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardModifierKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.GamepadButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.IRpcAudioChannel":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcAudioChannel.VoiceStates":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcMessageChannel":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcMessageChannel.CachedMessages":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcPrivateChannel":"Discord.Rpc.IRpcPrivateChannel.yml","Discord.Rpc.RpcChannel":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.Name":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.CreatedAt":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannelSummary":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Id":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Name":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Type":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.ToString":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcDMChannel":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CachedMessages":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.ToString":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IDMChannel#Recipient":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IPrivateChannel#Recipients":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#Name":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcGroupChannel":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.CachedMessages":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.VoiceStates":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.ToString":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#Name":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGuildChannel":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GuildId":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Position":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ToString":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#Guild":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#PermissionOverwrites":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcTextChannel":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.CachedMessages":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Mention":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#ITextChannel#Topic":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcVoiceChannel":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.UserLimit":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.Bitrate":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.VoiceStates":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcGuild":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Name":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.IconUrl":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Users":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.ToString":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuildStatus":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Guild":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Online":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.ToString":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildSummary":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Id":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Name":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.ToString":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcMessage":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Channel":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Author":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Content":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.AuthorColor":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.CreatedAt":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsTTS":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsPinned":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsBlocked":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.EditedTimestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Attachments":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Embeds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedChannelIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedRoleIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedUserIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Tags":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.WebhookId":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsWebhook":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Timestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.DeleteAsync(Discord.RequestOptions)":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.ToString":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Type":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Author":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Attachments":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Embeds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcSystemMessage":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcSystemMessage.Type":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcUserMessage":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsTTS":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsPinned":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsBlocked":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.WebhookId":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.EditedTimestamp":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Attachments":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Embeds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedChannelIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedRoleIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedUserIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Tags":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Reactions":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.PinAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.Pan":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Left":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Right":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.#ctor(System.Single,System.Single)":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.ToString":"Discord.Rpc.Pan.yml","Discord.Rpc.RpcGuildUser":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcGuildUser.Status":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcUser":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.IsBot":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Username":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.DiscriminatorValue":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarId":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarUrl":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreatedAt":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discriminator":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Mention":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Game":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Status":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.ToString":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcVoiceState":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.User":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Nickname":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Volume":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted2":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Pan":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSuppressed":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.ToString":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Discord#IVoiceState#VoiceSessionId":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Discord#IVoiceState#VoiceChannel":"Discord.Rpc.RpcVoiceState.yml","Discord.WebSocket":"Discord.WebSocket.yml","Discord.WebSocket.DiscordSocketClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ShardId":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectionState":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Latency":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ApiClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUser":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Guilds":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.PrivateChannels":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor(Discord.WebSocket.DiscordSocketConfig)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLogoutAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectAsync(System.Boolean)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DisconnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetApplicationInfoAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuild(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetChannel(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetConnectionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetInviteAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetVoiceRegion(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadAllUsersAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(System.Collections.Generic.IEnumerable{Discord.IGuild})":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(Discord.IGuild[])":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetStatus(Discord.UserStatus)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetGame(System.String,System.String,Discord.StreamType)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#ApiClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#ConnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetUserAsync(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Connected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Disconnected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Ready":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LatencyUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelDestroyed":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageReceived":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionsCleared":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.JoinedGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LeftGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildAvailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUnavailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMembersDownloaded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserJoined":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserLeft":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserBanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUnbanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMemberUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserPresenceUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserVoiceStateUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserIsTyping":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketConfig":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.GatewayEncoding":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ConnectionTimeout":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ShardId":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.TotalShards":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.MessageCacheSize":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.LargeThreshold":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.AudioMode":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.WebSocketProvider":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.SocketEntity`1":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Discord":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Id":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.ISocketAudioChannel":"Discord.WebSocket.ISocketAudioChannel.yml","Discord.WebSocket.ISocketMessageChannel":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.CachedMessages":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketPrivateChannel":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.ISocketPrivateChannel.Recipients":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.SocketChannel":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.CreatedAt":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Users":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#Name":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketDMChannel":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Recipient":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CachedMessages":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Users":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.ToString":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IDMChannel#Recipient":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IPrivateChannel#Recipients":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#Name":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketGroupChannel":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Name":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.CachedMessages":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Users":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.ToString":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGuildChannel":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Guild":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Name":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Position":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.PermissionOverwrites":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Users":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ToString":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#Guild":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GuildId":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketTextChannel":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Topic":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Mention":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.CachedMessages":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Users":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketVoiceChannel":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Bitrate":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.UserLimit":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Users":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketGuild":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Name":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKTimeout":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsEmbeddable":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VerificationLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MfaLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultMessageNotifications":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadedMemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AudioClient":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EmbedChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.OwnerId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VoiceRegionId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreatedAt":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.HasAllMembers":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsSynced":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SyncPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloaderPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CurrentUser":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EveryoneRole":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Channels":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Emojis":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Features":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Users":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Roles":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VoiceStates":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.LeaveAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetBansAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetChannel(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetRole(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadUsersAsync":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DisconnectAudioAsync(Discord.Audio.AudioClient)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ToString":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#Available":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#AudioClient":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#EveryoneRole":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#Roles":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetBansAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetIntegrationsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetRole(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#DownloadUsersAsync":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketMessage":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Author":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Channel":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Content":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.CreatedAt":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsTTS":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsPinned":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.EditedTimestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Attachments":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Embeds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedChannels":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedRoles":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedUsers":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Tags":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.WebhookId":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsWebhook":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Timestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.ToString":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Author":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Channel":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Type":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Attachments":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Embeds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedChannelIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedRoleIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedUserIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketReaction":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.UserId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.User":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.MessageId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Message":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Channel":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Emoji":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketUserMessage":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsTTS":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsPinned":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.WebhookId":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.EditedTimestamp":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Attachments":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Embeds":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Tags":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedChannels":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedRoles":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedUsers":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Reactions":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.PinAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketRole":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Guild":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Color":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsHoisted":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsManaged":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsMentionable":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Name":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Permissions":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Position":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CreatedAt":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsEveryone":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Mention":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ToString":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Discord#IRole#Guild":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CompareTo(Discord.IRole)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketGroupUser":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Channel":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.IsBot":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Username":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.DiscriminatorValue":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.AvatarId":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsDeafened":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsMuted":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSelfDeafened":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSelfMuted":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSuppressed":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#VoiceSessionId":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGuildUser":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Guild":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Nickname":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsBot":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Username":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.DiscriminatorValue":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.AvatarId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GuildPermissions":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSuppressed":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.JoinedAt":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.RoleIds":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceChannel":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceSessionId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceState":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Hierarchy":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.KickAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#Guild":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#GuildId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#RoleIds":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketPresence":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Status":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Game":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.ToString":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketSelfUser":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Email":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsVerified":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsMfaEnabled":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsBot":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Username":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.DiscriminatorValue":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.AvatarId":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSimpleUser":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.IsBot":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.Username":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.DiscriminatorValue":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.AvatarId":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketUser":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.IsBot":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Username":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.DiscriminatorValue":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarId":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarUrl":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreatedAt":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discriminator":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Mention":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Game":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Status":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.ToString":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketVoiceState":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceChannel":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceSessionId":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSuppressed":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.ToString":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketVoiceState.yml","Discord.Commands":"Discord.Commands.yml","Discord.Commands.CommandContext":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Client":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Guild":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Channel":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.User":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Message":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.IsPrivate":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.#ctor(Discord.IDiscordClient,Discord.IGuild,Discord.IMessageChannel,Discord.IUser,Discord.IUserMessage)":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.#ctor(Discord.IDiscordClient,Discord.IUserMessage)":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandError":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnknownCommand":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ParseFailed":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.BadArgCount":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ObjectNotFound":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.MultipleMatches":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnmetPrecondition":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.Exception":"Discord.Commands.CommandError.yml","Discord.Commands.CommandService":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Modules":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Commands":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor(Discord.Commands.CommandServiceConfig)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.CreateModuleAsync(System.String,System.Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModulesAsync(System.Reflection.Assembly)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync(Discord.Commands.ModuleInfo)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader``1(Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader(System.Type,Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.Commands.CommandContext,System.Int32)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.Commands.CommandContext,System.String)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(Discord.Commands.CommandContext,System.Int32,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(Discord.Commands.CommandContext,System.String,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandServiceConfig":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.DefaultRunMode":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.CaseSensitiveCommands":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.ModuleBase":"Discord.Commands.ModuleBase.yml","Discord.Commands.ModuleBase.Context":"Discord.Commands.ModuleBase.yml","Discord.Commands.ModuleBase.ReplyAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Commands.ModuleBase.yml","Discord.Commands.MultiMatchHandling":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Exception":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Best":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.RunMode":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Default":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Sync":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Mixed":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Async":"Discord.Commands.RunMode.yml","Discord.Commands.AliasAttribute":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.Aliases":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.#ctor(System.String[])":"Discord.Commands.AliasAttribute.yml","Discord.Commands.CommandAttribute":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.Text":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.RunMode":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor(System.String)":"Discord.Commands.CommandAttribute.yml","Discord.Commands.DontAutoLoadAttribute":"Discord.Commands.DontAutoLoadAttribute.yml","Discord.Commands.GroupAttribute":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.Prefix":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor(System.String)":"Discord.Commands.GroupAttribute.yml","Discord.Commands.NameAttribute":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.Text":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.#ctor(System.String)":"Discord.Commands.NameAttribute.yml","Discord.Commands.PreconditionAttribute":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PreconditionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PriorityAttribute":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.Priority":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.#ctor(System.Int32)":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.RemainderAttribute":"Discord.Commands.RemainderAttribute.yml","Discord.Commands.RemarksAttribute":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.Text":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.#ctor(System.String)":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.SummaryAttribute":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.Text":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.#ctor(System.String)":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.RequireBotPermissionAttribute":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.GuildPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.ChannelPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(Discord.GuildPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(Discord.ChannelPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.ContextType":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Guild":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.DM":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Group":"Discord.Commands.ContextType.yml","Discord.Commands.RequireContextAttribute":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.Contexts":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.#ctor(Discord.Commands.ContextType)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireOwnerAttribute":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireOwnerAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireUserPermissionAttribute":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.GuildPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.ChannelPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(Discord.GuildPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(Discord.ChannelPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.DependencyMap":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Empty":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.#ctor":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Add``1(``0)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get``1":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get(System.Type)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet``1(``0@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.IDependencyMap":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Add``1(``0)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get``1":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet``1(``0@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get(System.Type)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IEnumerableExtensions":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.IEnumerableExtensions.Permutate``3(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEnumerable{``1},System.Func{``0,``1,``2})":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.MessageExtensions":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasCharPrefix(Discord.IUserMessage,System.Char,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasStringPrefix(Discord.IUserMessage,System.String,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasMentionPrefix(Discord.IUserMessage,Discord.IUser,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.CommandInfo":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Module":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Name":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Summary":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Remarks":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Priority":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.HasVarArgs":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.RunMode":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Aliases":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Parameters":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Preconditions":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.CheckPreconditionsAsync(Discord.Commands.CommandContext,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ParseAsync(Discord.Commands.CommandContext,Discord.Commands.SearchResult,System.Nullable{Discord.Commands.PreconditionResult})":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Execute(Discord.Commands.CommandContext,Discord.Commands.ParseResult,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ExecuteAsync(Discord.Commands.CommandContext,System.Collections.Generic.IEnumerable{System.Object},System.Collections.Generic.IEnumerable{System.Object},Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.ModuleInfo":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Service":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Name":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Summary":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Remarks":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Aliases":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Commands":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Preconditions":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Submodules":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ParameterInfo":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Command":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Name":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Summary":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsOptional":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsRemainder":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsMultiple":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Type":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.DefaultValue":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Parse(Discord.Commands.CommandContext,System.String)":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.ToString":"Discord.Commands.ParameterInfo.yml","Discord.Commands.TypeReader":"Discord.Commands.TypeReader.yml","Discord.Commands.TypeReader.Read(Discord.Commands.CommandContext,System.String)":"Discord.Commands.TypeReader.yml","Discord.Commands.ExecuteResult":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Exception":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Error":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ErrorReason":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.IsSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(System.Exception)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ToString":"Discord.Commands.ExecuteResult.yml","Discord.Commands.IResult":"Discord.Commands.IResult.yml","Discord.Commands.IResult.Error":"Discord.Commands.IResult.yml","Discord.Commands.IResult.ErrorReason":"Discord.Commands.IResult.yml","Discord.Commands.IResult.IsSuccess":"Discord.Commands.IResult.yml","Discord.Commands.ParseResult":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ArgValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ParamValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.Error":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ErrorReason":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.IsSuccess":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderResult},System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderResult})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderValue},System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderValue})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ToString":"Discord.Commands.ParseResult.yml","Discord.Commands.PreconditionResult":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.Error":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ErrorReason":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.IsSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(System.String)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(Discord.Commands.IResult)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ToString":"Discord.Commands.PreconditionResult.yml","Discord.Commands.SearchResult":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Text":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Commands":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Error":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ErrorReason":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.IsSuccess":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromSuccess(System.String,System.Collections.Generic.IReadOnlyList{Discord.Commands.CommandInfo})":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.IResult)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ToString":"Discord.Commands.SearchResult.yml","Discord.Commands.TypeReaderValue":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Value":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Score":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.#ctor(System.Object,System.Single)":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.ToString":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderResult":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Values":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Error":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ErrorReason":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.IsSuccess":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Object)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(Discord.Commands.TypeReaderValue)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Collections.Generic.IReadOnlyCollection{Discord.Commands.TypeReaderValue})":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.IResult)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ToString":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.Builders":"Discord.Commands.Builders.yml","Discord.Commands.Builders.CommandBuilder":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Module":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Name":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Summary":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Remarks":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.RunMode":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Priority":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Preconditions":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Parameters":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Aliases":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithName(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithSummary(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRunMode(Discord.Commands.RunMode)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithPriority(System.Int32)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddAliases(System.String[])":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddParameter(System.String,System.Type,System.Action{Discord.Commands.Builders.ParameterBuilder})":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.ModuleBuilder":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Service":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Parent":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Name":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Summary":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Remarks":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Commands":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Modules":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Preconditions":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Aliases":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithName(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddAlias(System.String[])":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddCommand(System.String,System.Func{Discord.Commands.CommandContext,System.Object[],Discord.Commands.IDependencyMap,System.Threading.Tasks.Task},System.Action{Discord.Commands.Builders.CommandBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddModule(System.String,System.Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Build(Discord.Commands.CommandService)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ParameterBuilder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Command":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Name":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.ParameterType":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.TypeReader":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsOptional":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsRemainder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsMultiple":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.DefaultValue":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Summary":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithDefault(System.Object)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsOptional(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsRemainder(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsMultiple(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml"} \ No newline at end of file diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 6a629f0be..0ef259067 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -3,82 +3,152 @@ [Discord.Commands](xref:Discord.Commands) provides an Attribute-based Command Parser. -### Setup +## Setup -To use Commands, you must create a -[Commands Service](xref:Discord.Commands.CommandService) -and a Command Handler. +To use Commands, you must create a [Commands Service] and a +Command Handler. Included below is a very bare-bones Command Handler. You can extend your Command Handler as much as you like, however the below is the bare minimum. -[!code-csharp[Barebones Command Handler](samples/command_handler.cs)] +The CommandService optionally will accept a [CommandServiceConfig], +which _does_ set a few default values for you. It is recommended to +look over the properties in [CommandServiceConfig], and their default +values. -## Commands +[!code-csharp[Command Handler](samples/command_handler.cs)] -In 1.0, Commands are no longer implemented at runtime with a builder -pattern. While a builder pattern may be provided later, commands are -created primarily with attributes. +[Command Service]: xref:Discord.Commands.CommandService +[CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig -### Basic Structure +## With Attributes -All commands belong to a Module. (See the below section for creating -modules). +In 1.0, Commands can be defined ahead of time, with attributes, or +at runtime, with builders. -All commands in a module must be defined as a `Task`. +For most bots, ahead-of-time commands should be all you need, and this +is the recommended method of defining commands. -To add parameters to your command, you simply need to add parameters -to the Task that represents the command. You are _not_ required to -accept all arguments as `String`, they will be automatically parsed -into the type you specify for the arument. See the Example Module -for an example of command parameters. +### Modules -## Modules +The first step to creating commands is to create a _module_. Modules are an organizational pattern that allow you to write your commands in different classes, and have them automatically loaded. -Discord.Net's implementation of Modules is influenced heavily from -ASP.Net Core's Controller pattern. This means that the lifetime of a -module instance is only as long as the command being ran in it. +Discord.Net's implementation of Modules is influenced heavily from +ASP.Net Core's Controller pattern. This means that the lifetime of a +module instance is only as long as the command being invoked. **Avoid using long-running code** in your modules whereever possible. -You should **not** be implementing very much logic into your modules; +You should **not** be implementing very much logic into your modules; outsource to a service for that. If you are unfamiliar with Inversion of Control, it is recommended to read the MSDN article on [IoC] and [Dependency Injection]. -To create a module, create a class that inherits from -@Discord.Commands.ModuleBase. +To begin, create a new class somewhere in your project, and +inherit the class from [ModuleBase]. This class **must** be `public`. + +>[!NOTE] +>[ModuleBase] is an _abstract_ class, meaning that you may extend it +>or override it as you see fit. Your module may inherit from any +>extension of ModuleBase. + +By now, your module should look like this: +[!code-csharp[Empty Module](samples/empty-module.cs)] [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx +[ModuleBase]: xref:Discord.Commands.ModuleBase + +### Adding Commands + +The next step to creating commands, is actually creating commands. + +To create a command, add a method to your module of type `Task`. +Typically, you will want to mark his method as `async`, although it is +not required. + +Adding parameters to a command is done by adding parameters to the +parent Task. + +For example, to take an integer as an argument, add `int arg`. To take +a user as an argument, add `IUser user`. In 1.0, a command can accept +nearly any type of argument; a full list of types that are parsed by +default can be found in the below section on _Type Readers_. + +Parameters, by default, are always required. To make a parameter +optional, give it a default value. To accept a comma-separated list, +set the parameter to `params Type[]`. + +Should a parameter include spaces, it **must** be wrapped in quotes. +For example, for a command with a parameter `string food`, you would +execute it with `!favoritefood "Key Lime Pie"`. + +If you would like a parameter to parse until the end of a command, +flag the parameter with the [RemainderAttribute]. This will allow a +user to invoke a command without wrapping a parameter in quotes. + +Finally, flag your command with the [CommandAttribute]. (You must +specify a name for this command, except for when it is part of a +module group - see below). + +[RemainderAttribute]: xref:Discord.Commands.RemainderAttribute +[CommandAttribute]: xref:Discord.Commands.CommandAttribute + +### Command Overloads + +You may add overloads of your commands, and the command parser will +automatically pick up on it. + +If, for whatever reason, you have too commands which are ambiguous to +each other, you may use the @Discord.Commands.PriorityAttribute to +specify which should be tested before the other. + +Priority's are sorted in ascending order; the higher priority will be +called first. + +### CommandContext + +Every command can access the execution context through the [Context] +property on [ModuleBase]. CommandContext allows you to access the +message, channel, guild, and user that the command was invoked from, +as well as the underlying discord client the command was invoked from. + +You may need to cast these objects to their Socket counterparts (see +the terminology section). -### Example Module +To reply to messages, you may also invoke [ReplyAsync], instead of +accessing the channel through the [Context] and sending a message. -[!code-csharp[Modules](samples/module.cs)] +### Example Module + +At this point, your module should look comparable to this example: +[!code-csharp[Example Module](samples/module.cs)] #### Loading Modules Automatically The Command Service can automatically discover all classes in an -Assembly that inherit @Discord.Commands.ModuleBase, and load them. +Assembly that inherit [ModuleBase], and load them. -To have a module opt-out of auto-loading, pass `autoload: false` in -the Module attribute. +To opt a module out of auto-loading, flag it with +[DontAutoLoadAttribute] -Invoke [CommandService.AddModules] to discover modules and install them. +Invoke [CommandService.AddModulesAsync] to discover modules and +install them. -[CommandService.AddModules]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModules +[DontAutoLoadAttribute]: Discord.Commands.DontAutoLoadAttribute +[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#AddModulesAsync_System_Reflection_Assembly_ #### Loading Modules Manually -To manually load a module, invoke [CommandService.AddModule], +To manually load a module, invoke [CommandService.AddModuleAsync], by passing in the generic type of your module, and optionally a dependency map. -[CommandService.AddModule]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModule__1_Discord_Commands_IDependencyMap_ +[CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1 ### Module Constructors @@ -87,18 +157,27 @@ that are placed in the constructor must be injected into an @Discord.Commands.IDependencyMap. Alternatively, you may accept an IDependencyMap as an argument and extract services yourself. -### Command Groups +### Module Groups -Command Groups allow you to create a module where commands are prefixed. -To create a group, create a new module and flag it with the -@Discord.Commands.GroupAttribute. +Module Groups allow you to create a module where commands are prefixed. +To create a group, flag a module with the +@Discord.Commands.GroupAttribute ->[!NOTE] ->Groups do not _need_ to be modules. Only classes with commands should ->inherit from ModuleBase. If you plan on using a group for strictly ->organizational purposes, there is no reason to make it a module. +Module groups also allow you to create **nameless commands**, where the +[CommandAttribute] is configured with no name. In this case, the +command will inherit the name of the group it belongs to. + +### Submodules + +Submodules are modules that reside within another module. Typically, +submodules are used to create nested groups (although not required to +create nested groups). + +[!code-csharp[Groups and Submodules](samples/groups.cs)] + +## With Builders -[!code-csharp[Groups Sample](samples/groups.cs)] +**TODO** ## Dependency Injection @@ -143,34 +222,13 @@ can be as complex as you want them to be. ## Bundled Preconditions -@Discord.Commands ships with two built-in preconditions, -@Discord.Commands.RequireContextAttribute and -@Discord.Commands.RequirePermissionAttribute. - -### RequireContext - -@Discord.Commands.RequireContextAttribute is a precondition that -requires your command to be executed in the specified context. - -You may require three different types of context: -* Guild -* DM -* Group - -Since these are `Flags`, you may OR them together. - -[!code-csharp[RequireContext](samples/require_context.cs)] +Commands ships with four bundled preconditions; you may view their +usages on their API page. -### RequirePermission - -@Discord.Commands.RequirePermissionAttribute is a precondition that -allows you to quickly specfiy that a user must poesess a permission -to execute a command. - -You may require either a @Discord.GuildPermission or -@Discord.ChannelPermission - -[!code-csharp[RequireContext](samples/require_permission.cs)] +- @Discord.Commands.RequireContextAttribute +- @Discord.Commands.RequireOwnerAttribute +- @Discord.Commands.RequireBotPermissionAttribute +- @Discord.Commands.RequireUserPermissionAttribute ## Custom Preconditions @@ -178,15 +236,20 @@ To write your own preconditions, create a new class that inherits from @Discord.Commands.PreconditionAttribute In order for your precondition to function, you will need to override -`CheckPermissions`, which is a `Task`. +[CheckPermissions]. + Your IDE should provide an option to fill this in for you. -Return `PreconditionResult.FromSuccess()` if the context met the -required parameters, otherwise return `PreconditionResult.FromError()`, +Return [PreconditionResult.FromSuccess] if the context met the +required parameters, otherwise return [PreconditionResult.FromError], optionally including an error message. [!code-csharp[Custom Precondition](samples/require_owner.cs)] +[CheckPermissions]: xref:Discord.Commands.PreconditionAttribute#Discord_Commands_PreconditionAttribute_CheckPermissions_Discord_Commands_CommandContext_Discord_Commands_CommandInfo_Discord_Commands_IDependencyMap_ +[PreconditionResult.FromSuccess]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromSuccess +[PreconditionResult.FromError]: xref:Discord.Commands.PreconditionResult#Discord_Commands_PreconditionResult_FromError_System_String_ + # Type Readers Type Readers allow you to parse different types of arguments in @@ -194,24 +257,26 @@ your commands. By default, the following Types are supported arguments: -- string +- bool +- char - sbyte/byte - ushort/short - uint/int - ulong/long - float, double, decimal -- DateTime/DateTimeOffset -- IUser/IGuildUser +- string +- DateTime/DateTimeOffset/TimeSpan +- IMessage/IUserMessage - IChannel/IGuildChannel/ITextChannel/IVoiceChannel/IGroupChannel +- IUser/IGuildUser/IGroupUser - IRole -- IMessage/IUserMessage ### Creating a Type Readers To create a TypeReader, create a new class that imports @Discord and @Discord.Commands. Ensure your class inherits from @Discord.Commands.TypeReader -Next, satisfy the `TypeReader` class by overriding `Task Read(CommandContext context, string input)`. +Next, satisfy the `TypeReader` class by overriding [Read]. >[!NOTE] >In many cases, Visual Studio can fill this in for you, using the @@ -223,6 +288,8 @@ Finally, return a `TypeReaderResult`. If you were able to successfully parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. Otherwise, return `TypeReaderResult.FromError`. +[Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_ + #### Sample [!code-csharp[TypeReaders](samples/typereader.cs)] diff --git a/docs/guides/intro.md b/docs/guides/intro.md index d02affe3d..5790734c1 100644 --- a/docs/guides/intro.md +++ b/docs/guides/intro.md @@ -23,11 +23,19 @@ You may add the MyGet feed to Visual Studio directly from `https://www.myget.org You can also pull the latest source from [GitHub](https://github.com/RogueException/Discord.Net). >[!WARNING] ->The versions of Discord.Net on NuGet are behind the versions this documentation is written for. +>The versions of Discord.Net on NuGet are behind the versions this +>documentation is written for. +>You MUST install from MyGet or Source! ## Async -Discord.Net uses C# tasks extensiely - nearly all operations return one. It is highly reccomended these tasks be awaited whenever possible. To do so requires the calling method to be marked as async, which can be problematic in a console application. An example of how to get around this is provided below. +Discord.Net uses C# tasks extensiely - nearly all operations return +one. + +It is highly reccomended these tasks be awaited whenever possible. +To do so requires the calling method to be marked as async, which +can be problematic in a console application. An example of how to +get around this is provided below. For more information, go to [MSDN's Async-Await section.](https://msdn.microsoft.com/en-us/library/hh191443.aspx) diff --git a/docs/guides/samples/command_handler.cs b/docs/guides/samples/command_handler.cs index 3ef3bfc6e..42dc47f17 100644 --- a/docs/guides/samples/command_handler.cs +++ b/docs/guides/samples/command_handler.cs @@ -8,6 +8,7 @@ public class Program { private CommandService commands; private DiscordSocketClient client; + private DependencyMap map; static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); @@ -18,6 +19,8 @@ public class Program string token = "bot token here"; + map = new DependencyMap(); + await InstallCommands(); await client.LoginAsync(TokenType.Bot, token); @@ -25,13 +28,12 @@ public class Program await Task.Delay(-1); } - public async Task InstallCommands() { // Hook the MessageReceived Event into our Command Handler client.MessageReceived += HandleCommand; // Discover all of the commands in this assembly and load them. - await commands.LoadAssembly(Assembly.GetEntryAssembly()); + await commands.AddModulesAsync(Assembly.GetEntryAssembly()); } public async Task HandleCommand(SocketMessage messageParam) { @@ -41,16 +43,14 @@ public class Program // Create a number to track where the prefix ends and the command begins int argPos = 0; // Determine if the message is a command, based on if it starts with '!' or a mention prefix - if (message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos)) - { - // Create a Command Context - var context = new CommandContext(client, message); - // Execute the command. (result does not indicate a return value, - // rather an object stating if the command executed succesfully) - var result = await _commands.Execute(context, argPos); - if (!result.IsSuccess) - await msg.Channel.SendMessageAsync(result.ErrorReason); - } + if (!(message.HasCharPrefix('!', ref argPos) || message.HasMentionPrefix(client.CurrentUser, ref argPos))) return; + // Create a Command Context + var context = new CommandContext(client, message); + // Execute the command. (result does not indicate a return value, + // rather an object stating if the command executed succesfully) + var result = await commands.ExecuteAsync(context, argPos, map); + if (!result.IsSuccess) + await msg.Channel.SendMessageAsync(result.ErrorReason); } } \ No newline at end of file diff --git a/docs/guides/samples/dependency_module.cs b/docs/guides/samples/dependency_module.cs index be7c980aa..2e1d662f8 100644 --- a/docs/guides/samples/dependency_module.cs +++ b/docs/guides/samples/dependency_module.cs @@ -2,28 +2,30 @@ using Discord; using Discord.Commands; using Discord.WebSocket; -[Module] -public class ModuleA +public class ModuleA : ModuleBase { - private DiscordSocketClient client; - private ISelfUser self; + private readonly DatabaseService _database; - public ModuleA(IDiscordClient c, ISelfUser s) + public ModuleA(DatabaseService database) { - if (!(c is DiscordSocketClient)) throw new InvalidOperationException("This module requires a DiscordSocketClient"); - client = c as DiscordSocketClient; - self = s; + _database = database; + } + + public async Task ReadFromDb() + { + var x = _database.getX(); + await ReplyAsync(x); } } public class ModuleB { - private IDiscordClient client; - private CommandService commands; + private CommandService _commands; + private NotificationService _notification; - public ModuleB(CommandService c, IDependencyMap m) + public ModuleB(CommandService commands, IDependencyMap map) { - commands = c; - client = m.Get(); + _commands = commands; + _notification = map.Get(); } } \ No newline at end of file diff --git a/docs/guides/samples/empty-module.cs b/docs/guides/samples/empty-module.cs new file mode 100644 index 000000000..cac9922b5 --- /dev/null +++ b/docs/guides/samples/empty-module.cs @@ -0,0 +1,6 @@ +using Discord.Commands; + +public class InfoModule : ModuleBase +{ + +} \ No newline at end of file diff --git a/docs/guides/samples/faq/send_message.cs b/docs/guides/samples/faq/send_message.cs index ed4ff9b8a..d7ecf5131 100644 --- a/docs/guides/samples/faq/send_message.cs +++ b/docs/guides/samples/faq/send_message.cs @@ -1,6 +1,6 @@ public async Task SendMessageToChannel(ulong ChannelId) { - var channel = _client.GetChannel(ChannelId) as ISocketMessageChannel; + var channel = _client.GetChannel(ChannelId) as SocketMessageChannel; await channel?.SendMessageAsync("aaaaaaaaahhh!!!") /* ^ This question mark is used to indicate that 'channel' may sometimes be null, and in cases that it is null, we will do nothing here. */ } \ No newline at end of file diff --git a/docs/guides/samples/groups.cs b/docs/guides/samples/groups.cs index b04148d26..db6456c87 100644 --- a/docs/guides/samples/groups.cs +++ b/docs/guides/samples/groups.cs @@ -1,15 +1,18 @@ [Group("admin")] public class AdminModule : ModuleBase { - [Group("mod")] - public class ModerationGroup : ModuleBase + [Group("clean")] + public class CleanModule : ModuleBase { - // ~admin mod ban foxbot#0282 - [Command("ban")] - public async Task Ban(IGuildUser user) { } - } + // ~admin clean 15 + [Command] + public async Task Default(int count = 10) => Messages(count); - // ~admin clean 100 - [Command("clean")] - public async Task Clean(int count = 100) { } + // ~admin clean messages 15 + [Command("messages")] + public async Task Messages(int count = 10) { } + } + // ~admin ban foxbot#0282 + [Command("ban")] + public async Task Ban(IGuildUser user) { } } \ No newline at end of file diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 451480761..5155bacdc 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -13,7 +13,11 @@ public class Program LogLevel = LogSeverity.Info }); - _client.Log += (message) => Console.WriteLine($"{message.ToString()}"); + _client.Log += (message) => + { + Console.WriteLine($"{message.ToString()}"); + return Task.CompletedTask; + }; await _client.LoginAsync(TokenType.Bot, "bot token"); } diff --git a/docs/guides/samples/require_context.cs b/docs/guides/samples/require_context.cs deleted file mode 100644 index 0bc558e4a..000000000 --- a/docs/guides/samples/require_context.cs +++ /dev/null @@ -1,10 +0,0 @@ -public class InfoModule : ModuleBase -{ - // Constrain this command to Guilds - [RequireContext(ContextType.Guild)] - public async Task Whois(IGuildUser user) { } - - // Constrain this command to either Guilds or DMs - [RequireContext(ContextType.Guild | ContextType.DM)] - public async Task Info() { } -} \ No newline at end of file diff --git a/docs/guides/samples/require_owner.cs b/docs/guides/samples/require_owner.cs index 670d92c19..c8d3c2071 100644 --- a/docs/guides/samples/require_owner.cs +++ b/docs/guides/samples/require_owner.cs @@ -1,4 +1,4 @@ -// Defining the Precondition +// (Note: This precondition is obsolete, it is recommended to use the RequireOwnerAttribute that is bundled with Discord.Commands) // Inherit from PreconditionAttribute public class RequireOwnerAttribute : PreconditionAttribute diff --git a/docs/guides/samples/require_permission.cs b/docs/guides/samples/require_permission.cs deleted file mode 100644 index 56a1ff744..000000000 --- a/docs/guides/samples/require_permission.cs +++ /dev/null @@ -1,6 +0,0 @@ -public class AdminModule : ModuleBase -{ - [Command("ban")] - [RequirePermission(GuildPermission.BanMembers)] - public async Task Ban(IGuildUser target) { } -} \ No newline at end of file diff --git a/docs/guides/samples/typereader.cs b/docs/guides/samples/typereader.cs index 69db3b991..b21e6c15a 100644 --- a/docs/guides/samples/typereader.cs +++ b/docs/guides/samples/typereader.cs @@ -1,3 +1,4 @@ +// Note: This example is obsolete, a boolean type reader is bundled with Discord.Commands using Discord; using Discord.Commands; diff --git a/docs/index.md b/docs/index.md index 6f5f4a1eb..b563a983d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ # Discord.Net Documentation -Refer to [Guides](guides/intro.md) for tutorials on using Discord.Net, or the [API documentation](api/index.md) to review individual objects in the library. +Refer to [The Intro](guides/intro.md) for tutorials on using Discord.Net, or the [API documentation](api/index.md) to review individual objects in the library. **Todo:** Put something meaningful here. \ No newline at end of file From d8c0f0aa4cc14d2d679b1cd44db35c0368b8cac9 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 3 Dec 2016 16:46:49 -0500 Subject: [PATCH 051/263] [docs] fix xrefs --- docs/CONTRIBUTING.md | 2 +- docs/guides/commands.md | 2 +- docs/guides/events.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 468ff7bd4..f19e7c297 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -10,6 +10,6 @@ I don't really have any strict conditions for writing documentation, but just ke ### Compiling -Documentation is compiled into a static site using [DocFx](dotnet.github.io/docfx/). You **must** install a version of DocFx that supports .NET Core. The latest build of that is [2.1.0-cli-alpha](https://github.com/dotnet/docfx/releases/tag/v2.1.0-cli-alpha). +Documentation is compiled into a static site using [DocFx](https://dotnet.github.io/docfx/). We currently use version 2.8 After making changes, compile your changes into the static site with `docfx`. You can also view your changes live with `docfx --serve`. \ No newline at end of file diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 0ef259067..8c33527ea 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -139,7 +139,7 @@ To opt a module out of auto-loading, flag it with Invoke [CommandService.AddModulesAsync] to discover modules and install them. -[DontAutoLoadAttribute]: Discord.Commands.DontAutoLoadAttribute +[DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute [CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#AddModulesAsync_System_Reflection_Assembly_ #### Loading Modules Manually diff --git a/docs/guides/events.md b/docs/guides/events.md index 310b011e6..b10dc7648 100644 --- a/docs/guides/events.md +++ b/docs/guides/events.md @@ -17,7 +17,7 @@ To hook into events, you must be using the @Discord.WebSocket.DiscordSocketClien Connection Events will be raised when the Connection State of your client changes. -[DiscordSocketClient.Connected](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Connected) and [Disconnected](Discord_WebSocket_DiscordSocketClient_Disconnected) are raised when the Gateway Socket connects or disconnects, respectively. +[DiscordSocketClient.Connected](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Connected) and [Disconnected](xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_Disconnected) are raised when the Gateway Socket connects or disconnects, respectively. >[!WARNING] >You should not use DiscordClient.Connected to run code when your client first connects to Discord. The client has not received and parsed the READY event and guild stream yet, and will have an incomplete or empty cache. From b7a5ee6542477d3e3b1a73443ba55b1f033e7aea Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 19 Nov 2016 15:12:04 +0000 Subject: [PATCH 052/263] Parameter preconditions and typereader overriding --- .../Attributes/OverrideTypeReaderAttribute.cs | 22 ++++++++++ .../ParameterPreconditionAttribute.cs | 11 +++++ .../Builders/ModuleClassBuilder.cs | 4 ++ .../Builders/ParameterBuilder.cs | 18 +++++++- src/Discord.Net.Commands/Info/CommandInfo.cs | 19 ++++++++- .../Info/ParameterInfo.cs | 42 +++++++++++++++---- 6 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs create mode 100644 src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs new file mode 100644 index 000000000..e14be063f --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs @@ -0,0 +1,22 @@ +using System; + +using System.Reflection; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Parameter)] + public class OverrideTypeReaderAttribute : Attribute + { + private readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); + + public Type TypeReader { get; } + + public OverrideTypeReaderAttribute(Type overridenType) + { + if (!_typeReaderTypeInfo.IsAssignableFrom(overridenType.GetTypeInfo())) + throw new ArgumentException($"{nameof(overridenType)} must inherit from {nameof(TypeReader)}"); + + TypeReader = overridenType; + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs new file mode 100644 index 000000000..f2ef78c05 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] + public abstract class ParameterPreconditionAttribute : Attribute + { + public abstract Task CheckPermissions(CommandContext context, ParameterInfo parameter, object value); + } +} \ No newline at end of file diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index aaec43161..1775cc1fe 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -182,6 +182,10 @@ namespace Discord.Commands // TODO: C#7 type switch if (attribute is SummaryAttribute) builder.Summary = (attribute as SummaryAttribute).Text; + else if (attribute is OverrideTypeReaderAttribute) + builder.TypeReader = service.GetTypeReader((attribute as OverrideTypeReaderAttribute).TypeReader); + else if (attribute is ParameterPreconditionAttribute) + builder.AddPrecondition(attribute as ParameterPreconditionAttribute); else if (attribute is ParamArrayAttribute) { builder.IsMultiple = true; diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs index 801a10080..6b941a1c7 100644 --- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs @@ -1,10 +1,14 @@ using System; using System.Reflection; +using System.Collections.Generic; + namespace Discord.Commands.Builders { public class ParameterBuilder { + private readonly List _preconditions; + public CommandBuilder Command { get; } public string Name { get; internal set; } public Type ParameterType { get; internal set; } @@ -16,16 +20,20 @@ namespace Discord.Commands.Builders public object DefaultValue { get; set; } public string Summary { get; set; } + public IReadOnlyList Preconditions => _preconditions; + //Automatic internal ParameterBuilder(CommandBuilder command) { + _preconditions = new List(); + Command = command; } //User-defined internal ParameterBuilder(CommandBuilder command, string name, Type type) : this(command) { - Preconditions.NotNull(name, nameof(name)); + Discord.Preconditions.NotNull(name, nameof(name)); Name = name; SetType(type); @@ -49,7 +57,7 @@ namespace Discord.Commands.Builders } public ParameterBuilder WithDefault(object defaultValue) { - DefaultValue = defaultValue; + DefaultValue = defaultValue; return this; } public ParameterBuilder WithIsOptional(bool isOptional) @@ -68,6 +76,12 @@ namespace Discord.Commands.Builders return this; } + public ParameterBuilder AddPrecondition(ParameterPreconditionAttribute precondition) + { + _preconditions.Add(precondition); + return this; + } + internal ParameterInfo Build(CommandInfo info) { if (TypeReader == null) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 571c47e13..a91a1f9f4 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -135,9 +135,26 @@ namespace Discord.Commands if (map == null) map = DependencyMap.Empty; + object[] args = null; + + try + { + args = GenerateArgs(argList, paramList); + } + catch (Exception ex) + { + return ExecuteResult.FromError(ex); + } + + foreach (var parameter in Parameters) + { + var result = await parameter.CheckPreconditionsAsync(context, args, map).ConfigureAwait(false); + if (!result.IsSuccess) + return ExecuteResult.FromError(result); + } + try { - var args = GenerateArgs(argList, paramList); switch (RunMode) { case RunMode.Sync: //Always sync diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index 18c5e653c..2ef4b89c4 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -1,5 +1,7 @@ using System; using System.Linq; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading.Tasks; using Discord.Commands.Builders; @@ -10,6 +12,17 @@ namespace Discord.Commands { private readonly TypeReader _reader; + public CommandInfo Command { get; } + public string Name { get; } + public string Summary { get; } + public bool IsOptional { get; } + public bool IsRemainder { get; } + public bool IsMultiple { get; } + public Type Type { get; } + public object DefaultValue { get; } + + public IReadOnlyList Preconditions { get; } + internal ParameterInfo(ParameterBuilder builder, CommandInfo command, CommandService service) { Command = command; @@ -23,17 +36,30 @@ namespace Discord.Commands Type = builder.ParameterType; DefaultValue = builder.DefaultValue; + Preconditions = builder.Preconditions.ToImmutableArray(); + _reader = builder.TypeReader; } - public CommandInfo Command { get; } - public string Name { get; } - public string Summary { get; } - public bool IsOptional { get; } - public bool IsRemainder { get; } - public bool IsMultiple { get; } - public Type Type { get; } - public object DefaultValue { get; } + public async Task CheckPreconditionsAsync(CommandContext context, object[] args, IDependencyMap map = null) + { + if (map == null) + map = DependencyMap.Empty; + + int position = 0; + for(position = 0; position < Command.Parameters.Count; position++) + if (Command.Parameters[position] == this) + break; + + foreach (var precondition in Preconditions) + { + var result = await precondition.CheckPermissions(context, this, args[position]).ConfigureAwait(false); + if (!result.IsSuccess) + return result; + } + + return PreconditionResult.FromSuccess(); + } public async Task Parse(CommandContext context, string input) { From f11f416024007f8f5dfa665167c142807506dee3 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 19 Nov 2016 21:09:49 +0000 Subject: [PATCH 053/263] Add IDependencyMap to parameter preconditions --- .../Attributes/ParameterPreconditionAttribute.cs | 2 +- src/Discord.Net.Commands/Info/ParameterInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs index f2ef78c05..3bf8d177a 100644 --- a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs @@ -6,6 +6,6 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] public abstract class ParameterPreconditionAttribute : Attribute { - public abstract Task CheckPermissions(CommandContext context, ParameterInfo parameter, object value); + public abstract Task CheckPermissions(CommandContext context, ParameterInfo parameter, object value, IDependencyMap map); } } \ No newline at end of file diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index 2ef4b89c4..f8a97647a 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -53,7 +53,7 @@ namespace Discord.Commands foreach (var precondition in Preconditions) { - var result = await precondition.CheckPermissions(context, this, args[position]).ConfigureAwait(false); + var result = await precondition.CheckPermissions(context, this, args[position], map).ConfigureAwait(false); if (!result.IsSuccess) return result; } From 156483bf717ca9d11d9331c08e4f32511b25a2a6 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 19 Nov 2016 21:11:30 +0000 Subject: [PATCH 054/263] Rename `overridenType` to `overridenTypeReader` The previous name was causing some confusion --- .../Attributes/OverrideTypeReaderAttribute.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs index e14be063f..8134b06b8 100644 --- a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs @@ -11,12 +11,12 @@ namespace Discord.Commands public Type TypeReader { get; } - public OverrideTypeReaderAttribute(Type overridenType) + public OverrideTypeReaderAttribute(Type overridenTypeReader) { - if (!_typeReaderTypeInfo.IsAssignableFrom(overridenType.GetTypeInfo())) - throw new ArgumentException($"{nameof(overridenType)} must inherit from {nameof(TypeReader)}"); + if (!_typeReaderTypeInfo.IsAssignableFrom(overridenTypeReader.GetTypeInfo())) + throw new ArgumentException($"{nameof(overridenTypeReader)} must inherit from {nameof(TypeReader)}"); - TypeReader = overridenType; + TypeReader = overridenTypeReader; } } } \ No newline at end of file From d2d7b4dce7bb578eefbf078428f1ae0b34f33437 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 19 Nov 2016 21:16:28 +0000 Subject: [PATCH 055/263] Make `_typeReaderTypeInfo` static Seems I missed this originally, whoops. --- .../Attributes/OverrideTypeReaderAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs index 8134b06b8..37f685c95 100644 --- a/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs @@ -7,7 +7,7 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Parameter)] public class OverrideTypeReaderAttribute : Attribute { - private readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); + private static readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo(); public Type TypeReader { get; } From 254e874c999336179dd878dd733cbcb53820cc03 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 21 Nov 2016 18:46:21 +0000 Subject: [PATCH 056/263] Fix OverrideTypeReader This commit also adds a TypeReaders property to CommandService, so it is possible to see all of the registered TypeReaders. This makes it possible for users to implement their own parsing instead of using the built-in parsing. --- .../Builders/ModuleClassBuilder.cs | 48 +++++++--- .../Builders/ParameterBuilder.cs | 7 +- src/Discord.Net.Commands/CommandService.cs | 87 ++++++++++--------- 3 files changed, 88 insertions(+), 54 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 1775cc1fe..aaa96fb8e 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -183,7 +183,9 @@ namespace Discord.Commands if (attribute is SummaryAttribute) builder.Summary = (attribute as SummaryAttribute).Text; else if (attribute is OverrideTypeReaderAttribute) - builder.TypeReader = service.GetTypeReader((attribute as OverrideTypeReaderAttribute).TypeReader); + { + builder.TypeReader = GetTypeReader(service, paramType, (attribute as OverrideTypeReaderAttribute).TypeReader); + } else if (attribute is ParameterPreconditionAttribute) builder.AddPrecondition(attribute as ParameterPreconditionAttribute); else if (attribute is ParamArrayAttribute) @@ -200,23 +202,47 @@ namespace Discord.Commands } } - var reader = service.GetTypeReader(paramType); - if (reader == null) + if (builder.TypeReader == null) { - var paramTypeInfo = paramType.GetTypeInfo(); - if (paramTypeInfo.IsEnum) + var readers = service.GetTypeReaders(paramType); + var reader = readers?.FirstOrDefault(); + + if (reader == null) { - reader = EnumTypeReader.GetReader(paramType); - service.AddTypeReader(paramType, reader); + var paramTypeInfo = paramType.GetTypeInfo(); + if (paramTypeInfo.IsEnum) + { + reader = EnumTypeReader.GetReader(paramType); + service.AddTypeReader(paramType, reader); + } + else + { + throw new InvalidOperationException($"{paramType.FullName} is not supported as a command parameter, are you missing a TypeReader?"); + } } - else + + builder.ParameterType = paramType; + builder.TypeReader = reader; + } + } + + private static TypeReader GetTypeReader(CommandService service, Type paramType, Type typeReaderType) + { + var readers = service.GetTypeReaders(paramType); + if (readers != null) + { + var reader = readers.FirstOrDefault(x => x.GetType() == typeReaderType); + if (reader != default(TypeReader)) { - throw new InvalidOperationException($"{paramType.FullName} is not supported as a command parameter, are you missing a TypeReader?"); + return reader; } } - builder.ParameterType = paramType; - builder.TypeReader = reader; + //could not find any registered type reader: try to create one + var typeReader = ReflectionUtils.CreateObject(typeReaderType.GetTypeInfo(), service, DependencyMap.Empty); + service.AddTypeReader(paramType, typeReader); + + return typeReader; } private static bool IsValidModuleDefinition(TypeInfo typeInfo) diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs index 6b941a1c7..89f89b3cf 100644 --- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Reflection; using System.Collections.Generic; @@ -41,7 +42,11 @@ namespace Discord.Commands.Builders internal void SetType(Type type) { - TypeReader = Command.Module.Service.GetTypeReader(type); + var readers = Command.Module.Service.GetTypeReaders(type); + if (readers == null) + throw new InvalidOperationException($"{type} does not have a TypeReader registered for it"); + + TypeReader = readers.FirstOrDefault(); if (type.GetTypeInfo().IsValueType) DefaultValue = Activator.CreateInstance(type); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b6659fea3..285a35432 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -14,8 +14,8 @@ namespace Discord.Commands public class CommandService { private readonly SemaphoreSlim _moduleLock; - private readonly ConcurrentDictionary _typedModuleDefs; - private readonly ConcurrentDictionary _typeReaders; + private readonly ConcurrentDictionary _typedModuleDefs; + private readonly ConcurrentDictionary> _typeReaders; private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; @@ -24,6 +24,7 @@ namespace Discord.Commands public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); + public ILookup TypeReaders => _typeReaders.SelectMany(x => x.Value, (a, value) => new {a.Key, value}).ToLookup(x => x.Key, x => x.value); public CommandService() : this(new CommandServiceConfig()) { } public CommandService(CommandServiceConfig config) @@ -32,41 +33,41 @@ namespace Discord.Commands _typedModuleDefs = new ConcurrentDictionary(); _moduleDefs = new ConcurrentBag(); _map = new CommandMap(); - _typeReaders = new ConcurrentDictionary + _typeReaders = new ConcurrentDictionary> { - [typeof(bool)] = new SimpleTypeReader(), - [typeof(char)] = new SimpleTypeReader(), - [typeof(string)] = new SimpleTypeReader(), - [typeof(byte)] = new SimpleTypeReader(), - [typeof(sbyte)] = new SimpleTypeReader(), - [typeof(ushort)] = new SimpleTypeReader(), - [typeof(short)] = new SimpleTypeReader(), - [typeof(uint)] = new SimpleTypeReader(), - [typeof(int)] = new SimpleTypeReader(), - [typeof(ulong)] = new SimpleTypeReader(), - [typeof(long)] = new SimpleTypeReader(), - [typeof(float)] = new SimpleTypeReader(), - [typeof(double)] = new SimpleTypeReader(), - [typeof(decimal)] = new SimpleTypeReader(), - [typeof(DateTime)] = new SimpleTypeReader(), - [typeof(DateTimeOffset)] = new SimpleTypeReader(), - [typeof(TimeSpan)] = new SimpleTypeReader(), - [typeof(IMessage)] = new MessageTypeReader(), - [typeof(IUserMessage)] = new MessageTypeReader(), - [typeof(IChannel)] = new ChannelTypeReader(), - [typeof(IDMChannel)] = new ChannelTypeReader(), - [typeof(IGroupChannel)] = new ChannelTypeReader(), - [typeof(IGuildChannel)] = new ChannelTypeReader(), - [typeof(IMessageChannel)] = new ChannelTypeReader(), - [typeof(IPrivateChannel)] = new ChannelTypeReader(), - [typeof(ITextChannel)] = new ChannelTypeReader(), - [typeof(IVoiceChannel)] = new ChannelTypeReader(), - - [typeof(IRole)] = new RoleTypeReader(), - - [typeof(IUser)] = new UserTypeReader(), - [typeof(IGroupUser)] = new UserTypeReader(), - [typeof(IGuildUser)] = new UserTypeReader(), + [typeof(bool)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(char)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(string)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(byte)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(sbyte)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(ushort)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(short)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(uint)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(int)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(ulong)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(long)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(float)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(double)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(decimal)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(DateTime)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(DateTimeOffset)] = new ConcurrentBag{new SimpleTypeReader()}, + + [typeof(IMessage)] = new ConcurrentBag{new MessageTypeReader()}, + [typeof(IUserMessage)] = new ConcurrentBag{new MessageTypeReader()}, + [typeof(IChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IDMChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IGroupChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IGuildChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IMessageChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IPrivateChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(ITextChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + [typeof(IVoiceChannel)] = new ConcurrentBag{new ChannelTypeReader()}, + + [typeof(IRole)] = new ConcurrentBag{new RoleTypeReader()}, + + [typeof(IUser)] = new ConcurrentBag{new UserTypeReader()}, + [typeof(IGroupUser)] = new ConcurrentBag{new UserTypeReader()}, + [typeof(IGuildUser)] = new ConcurrentBag{new UserTypeReader()}, }; _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; @@ -196,17 +197,19 @@ namespace Discord.Commands //Type Readers public void AddTypeReader(TypeReader reader) { - _typeReaders[typeof(T)] = reader; + var readers = _typeReaders.GetOrAdd(typeof(T), x => new ConcurrentBag()); + readers.Add(reader); } public void AddTypeReader(Type type, TypeReader reader) { - _typeReaders[type] = reader; + var readers = _typeReaders.GetOrAdd(type, x=> new ConcurrentBag()); + readers.Add(reader); } - internal TypeReader GetTypeReader(Type type) + internal IEnumerable GetTypeReaders(Type type) { - TypeReader reader; - if (_typeReaders.TryGetValue(type, out reader)) - return reader; + ConcurrentBag definedTypeReaders; + if (_typeReaders.TryGetValue(type, out definedTypeReaders)) + return definedTypeReaders; return null; } From 704b2b75f462641f0d69076acb9f0a884349ba13 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 25 Nov 2016 20:22:06 +0000 Subject: [PATCH 057/263] Fix changes after review --- .../Builders/ModuleClassBuilder.cs | 19 ++-- .../Builders/ParameterBuilder.cs | 2 +- src/Discord.Net.Commands/CommandService.cs | 94 ++++++++++--------- src/Discord.Net.Commands/Info/CommandInfo.cs | 23 ++--- 4 files changed, 66 insertions(+), 72 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index aaa96fb8e..e8dc60de8 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -183,9 +183,7 @@ namespace Discord.Commands if (attribute is SummaryAttribute) builder.Summary = (attribute as SummaryAttribute).Text; else if (attribute is OverrideTypeReaderAttribute) - { builder.TypeReader = GetTypeReader(service, paramType, (attribute as OverrideTypeReaderAttribute).TypeReader); - } else if (attribute is ParameterPreconditionAttribute) builder.AddPrecondition(attribute as ParameterPreconditionAttribute); else if (attribute is ParamArrayAttribute) @@ -204,8 +202,7 @@ namespace Discord.Commands if (builder.TypeReader == null) { - var readers = service.GetTypeReaders(paramType); - var reader = readers?.FirstOrDefault(); + var reader = service.GetDefaultTypeReader(paramType); if (reader == null) { @@ -229,20 +226,16 @@ namespace Discord.Commands private static TypeReader GetTypeReader(CommandService service, Type paramType, Type typeReaderType) { var readers = service.GetTypeReaders(paramType); + TypeReader reader = null; if (readers != null) - { - var reader = readers.FirstOrDefault(x => x.GetType() == typeReaderType); - if (reader != default(TypeReader)) - { + if (readers.TryGetValue(typeReaderType, out reader)) return reader; - } - } //could not find any registered type reader: try to create one - var typeReader = ReflectionUtils.CreateObject(typeReaderType.GetTypeInfo(), service, DependencyMap.Empty); - service.AddTypeReader(paramType, typeReader); + reader = ReflectionUtils.CreateObject(typeReaderType.GetTypeInfo(), service, DependencyMap.Empty); + service.AddTypeReader(paramType, reader); - return typeReader; + return reader; } private static bool IsValidModuleDefinition(TypeInfo typeInfo) diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs index 89f89b3cf..d4cf598ec 100644 --- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs @@ -46,7 +46,7 @@ namespace Discord.Commands.Builders if (readers == null) throw new InvalidOperationException($"{type} does not have a TypeReader registered for it"); - TypeReader = readers.FirstOrDefault(); + TypeReader = readers.FirstOrDefault().Value; if (type.GetTypeInfo().IsValueType) DefaultValue = Activator.CreateInstance(type); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 285a35432..3dc1ca5d6 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -14,8 +14,9 @@ namespace Discord.Commands public class CommandService { private readonly SemaphoreSlim _moduleLock; - private readonly ConcurrentDictionary _typedModuleDefs; - private readonly ConcurrentDictionary> _typeReaders; + private readonly ConcurrentDictionary _typedModuleDefs; + private readonly ConcurrentDictionary> _typeReaders; + private readonly ConcurrentDictionary _defaultTypeReaders; private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; @@ -24,7 +25,7 @@ namespace Discord.Commands public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); - public ILookup TypeReaders => _typeReaders.SelectMany(x => x.Value, (a, value) => new {a.Key, value}).ToLookup(x => x.Key, x => x.value); + public ILookup TypeReaders => _typeReaders.SelectMany(x => x.Value.Select(y => new {y.Key, y.Value})).ToLookup(x => x.Key, x => x.Value); public CommandService() : this(new CommandServiceConfig()) { } public CommandService(CommandServiceConfig config) @@ -33,41 +34,43 @@ namespace Discord.Commands _typedModuleDefs = new ConcurrentDictionary(); _moduleDefs = new ConcurrentBag(); _map = new CommandMap(); - _typeReaders = new ConcurrentDictionary> + _typeReaders = new ConcurrentDictionary>(); + + _defaultTypeReaders = new ConcurrentDictionary { - [typeof(bool)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(char)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(string)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(byte)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(sbyte)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(ushort)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(short)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(uint)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(int)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(ulong)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(long)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(float)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(double)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(decimal)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(DateTime)] = new ConcurrentBag{new SimpleTypeReader()}, - [typeof(DateTimeOffset)] = new ConcurrentBag{new SimpleTypeReader()}, + [typeof(bool)] = new SimpleTypeReader(), + [typeof(char)] = new SimpleTypeReader(), + [typeof(string)] = new SimpleTypeReader(), + [typeof(byte)] = new SimpleTypeReader(), + [typeof(sbyte)] = new SimpleTypeReader(), + [typeof(ushort)] = new SimpleTypeReader(), + [typeof(short)] = new SimpleTypeReader(), + [typeof(uint)] = new SimpleTypeReader(), + [typeof(int)] = new SimpleTypeReader(), + [typeof(ulong)] = new SimpleTypeReader(), + [typeof(long)] = new SimpleTypeReader(), + [typeof(float)] = new SimpleTypeReader(), + [typeof(double)] = new SimpleTypeReader(), + [typeof(decimal)] = new SimpleTypeReader(), + [typeof(DateTime)] = new SimpleTypeReader(), + [typeof(DateTimeOffset)] = new SimpleTypeReader(), - [typeof(IMessage)] = new ConcurrentBag{new MessageTypeReader()}, - [typeof(IUserMessage)] = new ConcurrentBag{new MessageTypeReader()}, - [typeof(IChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IDMChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IGroupChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IGuildChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IMessageChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IPrivateChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(ITextChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - [typeof(IVoiceChannel)] = new ConcurrentBag{new ChannelTypeReader()}, - - [typeof(IRole)] = new ConcurrentBag{new RoleTypeReader()}, - - [typeof(IUser)] = new ConcurrentBag{new UserTypeReader()}, - [typeof(IGroupUser)] = new ConcurrentBag{new UserTypeReader()}, - [typeof(IGuildUser)] = new ConcurrentBag{new UserTypeReader()}, + [typeof(IMessage)] = new MessageTypeReader(), + [typeof(IUserMessage)] = new MessageTypeReader(), + [typeof(IChannel)] = new ChannelTypeReader(), + [typeof(IDMChannel)] = new ChannelTypeReader(), + [typeof(IGroupChannel)] = new ChannelTypeReader(), + [typeof(IGuildChannel)] = new ChannelTypeReader(), + [typeof(IMessageChannel)] = new ChannelTypeReader(), + [typeof(IPrivateChannel)] = new ChannelTypeReader(), + [typeof(ITextChannel)] = new ChannelTypeReader(), + [typeof(IVoiceChannel)] = new ChannelTypeReader(), + + [typeof(IRole)] = new RoleTypeReader(), + + [typeof(IUser)] = new UserTypeReader(), + [typeof(IGroupUser)] = new UserTypeReader(), + [typeof(IGuildUser)] = new UserTypeReader(), }; _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; @@ -197,21 +200,28 @@ namespace Discord.Commands //Type Readers public void AddTypeReader(TypeReader reader) { - var readers = _typeReaders.GetOrAdd(typeof(T), x => new ConcurrentBag()); - readers.Add(reader); + var readers = _typeReaders.GetOrAdd(typeof(T), x => new ConcurrentDictionary()); + readers[reader.GetType()] = reader; } public void AddTypeReader(Type type, TypeReader reader) { - var readers = _typeReaders.GetOrAdd(type, x=> new ConcurrentBag()); - readers.Add(reader); + var readers = _typeReaders.GetOrAdd(type, x=> new ConcurrentDictionary()); + readers[reader.GetType()] = reader; } - internal IEnumerable GetTypeReaders(Type type) + internal IDictionary GetTypeReaders(Type type) { - ConcurrentBag definedTypeReaders; + ConcurrentDictionary definedTypeReaders; if (_typeReaders.TryGetValue(type, out definedTypeReaders)) return definedTypeReaders; return null; } + internal TypeReader GetDefaultTypeReader(Type type) + { + TypeReader reader; + if (_defaultTypeReaders.TryGetValue(type, out reader)) + return reader; + return null; + } //Execution public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a91a1f9f4..a6ac50005 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -135,26 +135,17 @@ namespace Discord.Commands if (map == null) map = DependencyMap.Empty; - object[] args = null; - try { - args = GenerateArgs(argList, paramList); - } - catch (Exception ex) - { - return ExecuteResult.FromError(ex); - } + object[] args = GenerateArgs(argList, paramList); - foreach (var parameter in Parameters) - { - var result = await parameter.CheckPreconditionsAsync(context, args, map).ConfigureAwait(false); - if (!result.IsSuccess) - return ExecuteResult.FromError(result); - } + foreach (var parameter in Parameters) + { + var result = await parameter.CheckPreconditionsAsync(context, args, map).ConfigureAwait(false); + if (!result.IsSuccess) + return ExecuteResult.FromError(result); + } - try - { switch (RunMode) { case RunMode.Sync: //Always sync From 1e55d30a65fecc2a6142cc39a039bd933851ec82 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 22 Oct 2016 13:24:46 -0400 Subject: [PATCH 058/263] Raise GuildMemberUpdated when a Nickname is modified --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 5cb228506..6f925e60a 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1451,7 +1451,7 @@ namespace Discord.WebSocket } await _userPresenceUpdatedEvent.InvokeAsync(guild, user, before, user.Presence).ConfigureAwait(false); - if (data.User.Username.IsSpecified || data.Roles.IsSpecified) + if (data.User.Username.IsSpecified || data.Roles.IsSpecified || data.Nick.IsSpecified) { var before2 = user.Clone(); await _guildMemberUpdatedEvent.InvokeAsync(before2, user).ConfigureAwait(false); From c80f73764c6423e7841de594832f909c02cb7182 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 22 Oct 2016 15:15:19 -0400 Subject: [PATCH 059/263] Properly implement UserUpdated for Username or Avatar Changes --- .../DiscordSocketClient.cs | 36 ++++++++++++------- .../Entities/Users/SocketGuildUser.cs | 4 +++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 6f925e60a..bad00d006 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1423,6 +1423,7 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Dispatch (PRESENCE_UPDATE)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); + if (data.GuildId.IsSpecified) { var guild = State.GetGuild(data.GuildId.Value); @@ -1437,24 +1438,35 @@ namespace Discord.WebSocket return; } - SocketPresence before; + SocketPresence beforePresence; + SocketGuildUser beforeGuild; + SocketGlobalUser before; var user = guild.GetUser(data.User.Id); if (user != null) { - before = user.Presence.Clone(); + + beforePresence = user.Presence.Clone(); + before = user.GlobalUser.Clone(); + beforeGuild = user.Clone(); user.Update(State, data); } else { - before = new SocketPresence(UserStatus.Offline, null); + beforePresence = new SocketPresence(UserStatus.Offline, null); user = guild.AddOrUpdateUser(data); + before = user.GlobalUser.Clone(); + beforeGuild = user.Clone(); + } + + await _userPresenceUpdatedEvent.InvokeAsync(guild, user, beforePresence, user.Presence).ConfigureAwait(false); + if (data.Roles.IsSpecified || data.Nick.IsSpecified) + { + await _guildMemberUpdatedEvent.InvokeAsync(beforeGuild, user).ConfigureAwait(false); } - await _userPresenceUpdatedEvent.InvokeAsync(guild, user, before, user.Presence).ConfigureAwait(false); - if (data.User.Username.IsSpecified || data.Roles.IsSpecified || data.Nick.IsSpecified) + if (data.User.Username.IsSpecified || data.User.Avatar.IsSpecified) { - var before2 = user.Clone(); - await _guildMemberUpdatedEvent.InvokeAsync(before2, user).ConfigureAwait(false); + await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false); } } else @@ -1463,14 +1475,14 @@ namespace Discord.WebSocket if (channel != null) { var user = channel.GetUser(data.User.Id); - var before = user.Presence.Clone(); + var beforePresence = user.Presence.Clone(); + var before = user.Clone(); user.Update(State, data); - await _userPresenceUpdatedEvent.InvokeAsync(Optional.Create(), user, before, user.Presence).ConfigureAwait(false); - if (data.User.Username.IsSpecified) + await _userPresenceUpdatedEvent.InvokeAsync(Optional.Create(), user, beforePresence, user.Presence).ConfigureAwait(false); + if (data.User.Username.IsSpecified || data.User.Avatar.IsSpecified) { - var before2 = user.Clone(); - await _userUpdatedEvent.InvokeAsync(before2, user).ConfigureAwait(false); + await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 3ef45d230..b6804f89f 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -92,6 +92,10 @@ namespace Discord.WebSocket UpdateRoles(model.Roles.Value); if (model.Nick.IsSpecified) Nickname = model.Nick.Value; + if (model.User.Username.IsSpecified) + GlobalUser.Username = model.User.Username.Value; + if (model.User.Avatar.IsSpecified) + GlobalUser.AvatarId = model.User.Avatar.Value; } private void UpdateRoles(ulong[] roleIds) { From deafa3d755c10802728ff82f0512132fe6117f0f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 22 Oct 2016 15:22:35 -0400 Subject: [PATCH 060/263] save your work before committing *facepalm* --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index bad00d006..06ed64c8a 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1440,13 +1440,13 @@ namespace Discord.WebSocket SocketPresence beforePresence; SocketGuildUser beforeGuild; - SocketGlobalUser before; + SocketGlobalUser beforeGlobal; var user = guild.GetUser(data.User.Id); if (user != null) { beforePresence = user.Presence.Clone(); - before = user.GlobalUser.Clone(); + beforeGlobal = user.GlobalUser.Clone(); beforeGuild = user.Clone(); user.Update(State, data); } @@ -1454,7 +1454,7 @@ namespace Discord.WebSocket { beforePresence = new SocketPresence(UserStatus.Offline, null); user = guild.AddOrUpdateUser(data); - before = user.GlobalUser.Clone(); + beforeGlobal = user.GlobalUser.Clone(); beforeGuild = user.Clone(); } @@ -1466,7 +1466,7 @@ namespace Discord.WebSocket if (data.User.Username.IsSpecified || data.User.Avatar.IsSpecified) { - await _userUpdatedEvent.InvokeAsync(before, user).ConfigureAwait(false); + await _userUpdatedEvent.InvokeAsync(beforeGlobal, user).ConfigureAwait(false); } } else @@ -1476,7 +1476,7 @@ namespace Discord.WebSocket { var user = channel.GetUser(data.User.Id); var beforePresence = user.Presence.Clone(); - var before = user.Clone(); + var before = user.GlobalUser.Clone(); user.Update(State, data); await _userPresenceUpdatedEvent.InvokeAsync(Optional.Create(), user, beforePresence, user.Presence).ConfigureAwait(false); From dd9ae6e870c38151f445bcd582c7a2e1a8964719 Mon Sep 17 00:00:00 2001 From: Evan Sours Date: Sat, 3 Dec 2016 17:02:08 -0700 Subject: [PATCH 061/263] Fixed HandleCommand Error message if (!result.IsSuccess) await msg.Channel.SendMessageAsync(result.ErrorReason); } msg -> context --- docs/guides/samples/command_handler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/samples/command_handler.cs b/docs/guides/samples/command_handler.cs index 42dc47f17..71869415b 100644 --- a/docs/guides/samples/command_handler.cs +++ b/docs/guides/samples/command_handler.cs @@ -50,7 +50,7 @@ public class Program // rather an object stating if the command executed succesfully) var result = await commands.ExecuteAsync(context, argPos, map); if (!result.IsSuccess) - await msg.Channel.SendMessageAsync(result.ErrorReason); + await context.Channel.SendMessageAsync(result.ErrorReason); } -} \ No newline at end of file +} From 0e43d6c6f552337c35a1ce474b9827a703d7f6bf Mon Sep 17 00:00:00 2001 From: Evan Sours Date: Sat, 3 Dec 2016 17:44:32 -0700 Subject: [PATCH 062/263] Fixed Install -> commands.LoadAssembly await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); -> await commands.AddModulesAsync(Assembly.GetEntryAssembly()); --- docs/guides/samples/dependency_map_setup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/samples/dependency_map_setup.cs b/docs/guides/samples/dependency_map_setup.cs index af791990e..1d25db43c 100644 --- a/docs/guides/samples/dependency_map_setup.cs +++ b/docs/guides/samples/dependency_map_setup.cs @@ -11,7 +11,7 @@ public class Commands var map = new DependencyMap(); map.Add(client); map.Add(commands); - await commands.LoadAssembly(Assembly.GetCurrentAssembly(), map); + await commands.AddModulesAsync(Assembly.GetEntryAssembly()); } // In ConfigureServices, we will inject the Dependency Map with // all of the services our client will use. @@ -21,4 +21,4 @@ public class Commands map.Add(new DatabaseService(map)); } // ... -} \ No newline at end of file +} From cdb723bbeb343d7726576ffab322ddcf51a2a87d Mon Sep 17 00:00:00 2001 From: Evan Sours Date: Sat, 3 Dec 2016 18:25:31 -0700 Subject: [PATCH 063/263] Fixed async method CheckPermissions was not set to async so await would not work. --- docs/guides/samples/require_owner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/samples/require_owner.cs b/docs/guides/samples/require_owner.cs index c8d3c2071..567b3d2af 100644 --- a/docs/guides/samples/require_owner.cs +++ b/docs/guides/samples/require_owner.cs @@ -4,7 +4,7 @@ public class RequireOwnerAttribute : PreconditionAttribute { // Override the CheckPermissions method - public override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + public async override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) { // Get the ID of the bot's owner var ownerId = (await map.Get().GetApplicationInfoAsync()).Owner.Id; @@ -15,4 +15,4 @@ public class RequireOwnerAttribute : PreconditionAttribute else return PreconditionResult.FromError("You must be the owner of the bot to run this command."); } -} \ No newline at end of file +} From 8db026c0fe962e9f70c88628e4ddd3d456684dbb Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 3 Dec 2016 21:11:47 -0500 Subject: [PATCH 064/263] Make changes per feedback, remove invocations of GuildMemberUpdated Nickname changes/role changes are no longer sent over PRESENCE_UPDATE, making it obsolete to check for changes there. --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 12 ++---------- .../Entities/Users/SocketGuildUser.cs | 5 +---- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 06ed64c8a..0d58d6222 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1439,15 +1439,12 @@ namespace Discord.WebSocket } SocketPresence beforePresence; - SocketGuildUser beforeGuild; SocketGlobalUser beforeGlobal; var user = guild.GetUser(data.User.Id); if (user != null) { - beforePresence = user.Presence.Clone(); beforeGlobal = user.GlobalUser.Clone(); - beforeGuild = user.Clone(); user.Update(State, data); } else @@ -1455,19 +1452,14 @@ namespace Discord.WebSocket beforePresence = new SocketPresence(UserStatus.Offline, null); user = guild.AddOrUpdateUser(data); beforeGlobal = user.GlobalUser.Clone(); - beforeGuild = user.Clone(); - } - - await _userPresenceUpdatedEvent.InvokeAsync(guild, user, beforePresence, user.Presence).ConfigureAwait(false); - if (data.Roles.IsSpecified || data.Nick.IsSpecified) - { - await _guildMemberUpdatedEvent.InvokeAsync(beforeGuild, user).ConfigureAwait(false); } if (data.User.Username.IsSpecified || data.User.Avatar.IsSpecified) { await _userUpdatedEvent.InvokeAsync(beforeGlobal, user).ConfigureAwait(false); + return; } + await _userPresenceUpdatedEvent.InvokeAsync(guild, user, beforePresence, user.Presence).ConfigureAwait(false); } else { diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index b6804f89f..53475e5a2 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -88,14 +88,11 @@ namespace Discord.WebSocket internal override void Update(ClientState state, PresenceModel model) { base.Update(state, model); + GlobalUser.Update(state, model.User); if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); if (model.Nick.IsSpecified) Nickname = model.Nick.Value; - if (model.User.Username.IsSpecified) - GlobalUser.Username = model.User.Username.Value; - if (model.User.Avatar.IsSpecified) - GlobalUser.AvatarId = model.User.Avatar.Value; } private void UpdateRoles(ulong[] roleIds) { From d4d8e721db8b29dbb0c5063460bdcd6294a607b5 Mon Sep 17 00:00:00 2001 From: Aux Date: Mon, 28 Nov 2016 09:19:42 -0600 Subject: [PATCH 065/263] Resolves #390 Fix case insensitive commands forcing parameters to return lowercase --- src/Discord.Net.Commands/CommandService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b6659fea3..d5f4bee7e 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -214,9 +214,9 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); public SearchResult Search(CommandContext context, string input) { - input = _caseSensitive ? input : input.ToLowerInvariant(); - var matches = _map.GetCommands(input).OrderByDescending(x => x.Priority).ToImmutableArray(); - + string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); + var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Priority).ToImmutableArray(); + if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); else From 08cfc1dd7fa8180d813fa66ce4ddc97404ec458f Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 4 Dec 2016 17:21:05 -0400 Subject: [PATCH 066/263] Reconnect if heartbeat fails and a user download was not requested --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 5cb228506..5776d8470 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1659,7 +1659,7 @@ namespace Discord.WebSocket { if (_heartbeatTime != 0) //Server never responded to our last heartbeat { - if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? false)) + if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) { await _gatewayLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false); await StartReconnectAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false); From 64681856b1fd742cc7b53e22a5cfe9492b7a9617 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 4 Dec 2016 17:21:25 -0500 Subject: [PATCH 067/263] Create wrapper modify objects this was a big one --- .../API/DiscordRestApiClient.cs | 28 +++++++------- src/Discord.Net.Core/API/Image.cs | 5 +++ .../Channels/ModifyGuildChannelParams.cs | 8 ++++ .../Channels/ModifyGuildChannelsParams.cs | 14 +++++++ .../Channels/ModifyTextChannelParams.cs | 7 ++++ .../Channels/ModifyVoiceChannelParams.cs | 8 ++++ .../Entities/Guilds/ModifyGuildEmbedParams.cs | 8 ++++ .../Guilds/ModifyGuildIntegrationParams.cs | 9 +++++ .../Entities/Guilds/ModifyGuildParams.cs | 16 ++++++++ src/Discord.Net.Core/Entities/Image.cs | 17 +++++++++ .../Entities/Messages/EmbedBuilder.cs | 2 +- .../Entities/Roles/ModifyGuildRoleParams.cs | 11 ++++++ .../Entities/Roles/ModifyGuildRolesParams.cs | 12 ++++++ .../Users/ModifyCurrentUserNickParams.cs | 12 ++++++ .../Entities/Users/ModifyCurrentUserParams.cs | 8 ++++ .../Entities/Users/ModifyGuildMemberParams.cs | 11 ++++++ .../Net/Converters/ImageConverter.cs | 3 +- .../Entities/Channels/ChannelHelper.cs | 24 ++++++++++-- .../Entities/Guilds/GuildHelper.cs | 38 ++++++++++++++----- .../Entities/Guilds/RestGuildIntegration.cs | 8 +++- .../Entities/Roles/RoleHelper.cs | 10 ++++- .../Entities/Users/UserHelper.cs | 18 ++++++++- 22 files changed, 245 insertions(+), 32 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs create mode 100644 src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs create mode 100644 src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs create mode 100644 src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs create mode 100644 src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs create mode 100644 src/Discord.Net.Core/Entities/Image.cs create mode 100644 src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs create mode 100644 src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs create mode 100644 src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs create mode 100644 src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs create mode 100644 src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index bc7436cd4..167be4397 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -331,7 +331,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendAsync("DELETE", () => $"channels/{channelId}", ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildChannelAsync(ulong channelId, ModifyGuildChannelParams args, RequestOptions options = null) + public async Task ModifyGuildChannelAsync(ulong channelId, Rest.ModifyGuildChannelParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); @@ -342,7 +342,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("PATCH", () => $"channels/{channelId}", args, ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildChannelAsync(ulong channelId, ModifyTextChannelParams args, RequestOptions options = null) + public async Task ModifyGuildChannelAsync(ulong channelId, Rest.ModifyTextChannelParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); @@ -353,7 +353,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("PATCH", () => $"channels/{channelId}", args, ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildChannelAsync(ulong channelId, ModifyVoiceChannelParams args, RequestOptions options = null) + public async Task ModifyGuildChannelAsync(ulong channelId, Rest.ModifyVoiceChannelParams args, RequestOptions options = null) { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); @@ -366,7 +366,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendJsonAsync("PATCH", () => $"channels/{channelId}", args, ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable args, RequestOptions options = null) + public async Task ModifyGuildChannelsAsync(ulong guildId, IEnumerable args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); @@ -378,7 +378,7 @@ namespace Discord.API case 0: return; case 1: - await ModifyGuildChannelAsync(channels[0].Id, new ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); + await ModifyGuildChannelAsync(channels[0].Id, new Rest.ModifyGuildChannelParams { Position = channels[0].Position }).ConfigureAwait(false); break; default: var ids = new BucketIds(guildId: guildId); @@ -695,7 +695,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync("DELETE", () => $"users/@me/guilds/{guildId}", ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildAsync(ulong guildId, ModifyGuildParams args, RequestOptions options = null) + public async Task ModifyGuildAsync(ulong guildId, Rest.ModifyGuildParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); @@ -773,7 +773,7 @@ namespace Discord.API } catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } } - public async Task ModifyGuildEmbedAsync(ulong guildId, ModifyGuildEmbedParams args, RequestOptions options = null) + public async Task ModifyGuildEmbedAsync(ulong guildId, Rest.ModifyGuildEmbedParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotEqual(guildId, 0, nameof(guildId)); @@ -811,7 +811,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync("DELETE", () => $"guilds/{guildId}/integrations/{integrationId}", ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildIntegrationAsync(ulong guildId, ulong integrationId, ModifyGuildIntegrationParams args, RequestOptions options = null) + public async Task ModifyGuildIntegrationAsync(ulong guildId, ulong integrationId, Rest.ModifyGuildIntegrationParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(integrationId, 0, nameof(integrationId)); @@ -934,7 +934,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); await SendAsync("DELETE", () => $"guilds/{guildId}/members/{userId}", ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildMemberAsync(ulong guildId, ulong userId, ModifyGuildMemberParams args, RequestOptions options = null) + public async Task ModifyGuildMemberAsync(ulong guildId, ulong userId, Rest.ModifyGuildMemberParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(userId, 0, nameof(userId)); @@ -945,7 +945,7 @@ namespace Discord.API if (isCurrentUser && args.Nickname.IsSpecified) { - var nickArgs = new ModifyCurrentUserNickParams(args.Nickname.Value ?? ""); + var nickArgs = new Rest.ModifyCurrentUserNickParams(args.Nickname.Value ?? ""); await ModifyMyNickAsync(guildId, nickArgs).ConfigureAwait(false); args.Nickname = Optional.Create(); //Remove } @@ -982,7 +982,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); await SendAsync("DELETE", () => $"guilds/{guildId}/roles/{roleId}", ids, options: options).ConfigureAwait(false); } - public async Task ModifyGuildRoleAsync(ulong guildId, ulong roleId, ModifyGuildRoleParams args, RequestOptions options = null) + public async Task ModifyGuildRoleAsync(ulong guildId, ulong roleId, Rest.ModifyGuildRoleParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotEqual(roleId, 0, nameof(roleId)); @@ -995,7 +995,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendJsonAsync("PATCH", () => $"guilds/{guildId}/roles/{roleId}", args, ids, options: options).ConfigureAwait(false); } - public async Task> ModifyGuildRolesAsync(ulong guildId, IEnumerable args, RequestOptions options = null) + public async Task> ModifyGuildRolesAsync(ulong guildId, IEnumerable args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); Preconditions.NotNull(args, nameof(args)); @@ -1053,7 +1053,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); return await SendAsync("GET", () => "oauth2/applications/@me", new BucketIds(), options: options).ConfigureAwait(false); } - public async Task ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null) + public async Task ModifySelfAsync(Rest.ModifyCurrentUserParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotNullOrEmpty(args.Username, nameof(args.Username)); @@ -1061,7 +1061,7 @@ namespace Discord.API return await SendJsonAsync("PATCH", () => "users/@me", args, new BucketIds(), options: options).ConfigureAwait(false); } - public async Task ModifyMyNickAsync(ulong guildId, ModifyCurrentUserNickParams args, RequestOptions options = null) + public async Task ModifyMyNickAsync(ulong guildId, Rest.ModifyCurrentUserNickParams args, RequestOptions options = null) { Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args.Nickname, nameof(args.Nickname)); diff --git a/src/Discord.Net.Core/API/Image.cs b/src/Discord.Net.Core/API/Image.cs index 5442bd30f..44c58f344 100644 --- a/src/Discord.Net.Core/API/Image.cs +++ b/src/Discord.Net.Core/API/Image.cs @@ -17,5 +17,10 @@ namespace Discord.API Stream = null; Hash = hash; } + + public static Image Create(Discord.Image image) + { + return new Image(image.Stream); + } } } diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs new file mode 100644 index 000000000..2f1cce4b1 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public class ModifyGuildChannelParams + { + public Optional Name { get; set; } + public Optional Position { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs new file mode 100644 index 000000000..e46e172b9 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs @@ -0,0 +1,14 @@ +namespace Discord +{ + public class ModifyGuildChannelsParams + { + public ulong Id { get; set; } + public int Position { get; set; } + + public ModifyGuildChannelsParams(ulong id, int position) + { + Id = id; + Position = position; + } + } +} diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs new file mode 100644 index 000000000..fc5c2aad4 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs @@ -0,0 +1,7 @@ +namespace Discord +{ + public class ModifyTextChannelParams : ModifyGuildChannelParams + { + public Optional Topic { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs new file mode 100644 index 000000000..36941e03b --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public class ModifyVoiceChannelParams : ModifyGuildChannelParams + { + public Optional Bitrate { get; set; } + public Optional UserLimit { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs new file mode 100644 index 000000000..83a32a79c --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public class ModifyGuildEmbedParams + { + public Optional Enabled { get; set; } + public Optional ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs new file mode 100644 index 000000000..b9375d907 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs @@ -0,0 +1,9 @@ +namespace Discord +{ + public class ModifyGuildIntegrationParams + { + public Optional ExpireBehavior { get; set; } + public Optional ExpireGracePeriod { get; set; } + public Optional EnableEmoticons { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs new file mode 100644 index 000000000..19f2476f2 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs @@ -0,0 +1,16 @@ +namespace Discord +{ + public class ModifyGuildParams + { + public Optional Username { get; set; } + public Optional Name { get; set; } + public Optional RegionId { get; set; } + public Optional VerificationLevel { get; set; } + public Optional DefaultMessageNotifications { get; set; } + public Optional AfkTimeout { get; set; } + public Optional Icon { get; set; } + public Optional Splash { get; set; } + public Optional AfkChannelId { get; set; } + public Optional OwnerId { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs new file mode 100644 index 000000000..bb305f113 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace Discord +{ + public struct Image + { + public Stream Stream { get; } + public Image(Stream stream) + { + Stream = stream; + } + public Image(string path) + { + Stream = File.OpenRead(path); + } + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 40ef93e10..a6c9cfb8c 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -5,7 +5,7 @@ using Field = Discord.API.EmbedField; using Author = Discord.API.EmbedAuthor; using Footer = Discord.API.EmbedFooter; using Thumbnail = Discord.API.EmbedThumbnail; -using Image = Discord.API.EmbedImage; +using ImageEmbed = Discord.API.EmbedImage; namespace Discord { diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs new file mode 100644 index 000000000..486d56513 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs @@ -0,0 +1,11 @@ +namespace Discord +{ + public class ModifyGuildRoleParams + { + public Optional Name { get; set; } + public Optional Permissions { get; set; } + public Optional Position { get; set; } + public Optional Color { get; set; } + public Optional Hoist { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs new file mode 100644 index 000000000..4d6a0f63b --- /dev/null +++ b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs @@ -0,0 +1,12 @@ +namespace Discord +{ + public class ModifyGuildRolesParams : ModifyGuildRoleParams + { + public ulong Id { get; } + + public ModifyGuildRolesParams(ulong id) + { + Id = id; + } + } +} diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs new file mode 100644 index 000000000..c8d2b5199 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs @@ -0,0 +1,12 @@ +namespace Discord +{ + public class ModifyCurrentUserNickParams + { + public string Nickname { get; } + + public ModifyCurrentUserNickParams(string nickname) + { + Nickname = nickname; + } + } +} diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs new file mode 100644 index 000000000..cdd031c34 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs @@ -0,0 +1,8 @@ +namespace Discord +{ + public class ModifyCurrentUserParams + { + public Optional Username { get; set; } + public Optional Avatar { get; set; } + } +} diff --git a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs new file mode 100644 index 000000000..8e74d80e0 --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs @@ -0,0 +1,11 @@ +namespace Discord +{ + public class ModifyGuildMemberParams + { + public Optional Mute { get; set; } + public Optional Deaf { get; set; } + public Optional Nickname { get; set; } + public Optional RoleIds { get; set; } + public Optional ChannelId { get; set; } + } +} diff --git a/src/Discord.Net.Core/Net/Converters/ImageConverter.cs b/src/Discord.Net.Core/Net/Converters/ImageConverter.cs index 79e8c984d..e82b7952b 100644 --- a/src/Discord.Net.Core/Net/Converters/ImageConverter.cs +++ b/src/Discord.Net.Core/Net/Converters/ImageConverter.cs @@ -1,6 +1,7 @@ using Discord.API; using Newtonsoft.Json; using System; +using Model = Discord.API.Image; namespace Discord.Net.Converters { @@ -19,7 +20,7 @@ namespace Discord.Net.Converters public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { - var image = (Image)value; + var image = (Model)value; if (image.Stream != null) { diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 99edc6f48..9db89bb34 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -24,7 +24,12 @@ namespace Discord.Rest { var args = new ModifyGuildChannelParams(); func(args); - return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildChannelParams + { + Name = args.Name, + Position = args.Position + }; + return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client, Action func, @@ -32,7 +37,13 @@ namespace Discord.Rest { var args = new ModifyTextChannelParams(); func(args); - return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyTextChannelParams + { + Name = args.Name, + Position = args.Position, + Topic = args.Topic + }; + return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, Action func, @@ -40,7 +51,14 @@ namespace Discord.Rest { var args = new ModifyVoiceChannelParams(); func(args); - return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyVoiceChannelParams + { + Bitrate = args.Bitrate, + Name = args.Name, + Position = args.Position, + UserLimit = args.UserLimit + }; + return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } //Invites diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 7aaa19304..fb3e8e2cf 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using EmbedModel = Discord.API.GuildEmbed; using Model = Discord.API.Guild; using RoleModel = Discord.API.Role; +using ImageModel = Discord.API.Image; namespace Discord.Rest { @@ -21,12 +22,24 @@ namespace Discord.Rest var args = new ModifyGuildParams(); func(args); - if (args.Splash.IsSpecified && guild.SplashId != null) - args.Splash = new API.Image(guild.SplashId); - if (args.Icon.IsSpecified && guild.IconId != null) - args.Icon = new API.Image(guild.IconId); - - return await client.ApiClient.ModifyGuildAsync(guild.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildParams + { + AfkChannelId = args.AfkChannelId, + AfkTimeout = args.AfkTimeout, + DefaultMessageNotifications = args.DefaultMessageNotifications, + Name = args.Name, + OwnerId = args.OwnerId, + RegionId = args.RegionId, + Username = args.Username, + VerificationLevel = args.VerificationLevel + }; + + if (apiArgs.Splash.IsSpecified && guild.SplashId != null) + apiArgs.Splash = new ImageModel(guild.SplashId); + if (apiArgs.Icon.IsSpecified && guild.IconId != null) + apiArgs.Icon = new ImageModel(guild.IconId); + + return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, Action func, RequestOptions options) @@ -35,17 +48,24 @@ namespace Discord.Rest var args = new ModifyGuildEmbedParams(); func(args); - return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildEmbedParams + { + ChannelId = args.ChannelId, + Enabled = args.Enabled + }; + return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client, IEnumerable args, RequestOptions options) { - await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, args, options).ConfigureAwait(false); + var apiArgs = args.Select(x => new API.Rest.ModifyGuildChannelsParams(x.Id, x.Position)); + await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task> ModifyRolesAsync(IGuild guild, BaseDiscordClient client, IEnumerable args, RequestOptions options) { - return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, args, options).ConfigureAwait(false); + var apiArgs = args.Select(x => new API.Rest.ModifyGuildRolesParams(x.Id) { Color = x.Color, Hoist = x.Hoist, Name = x.Name, Permissions = x.Permissions, Position = x.Position }); + return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs index fc2bfd8b2..5405becc4 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs @@ -61,7 +61,13 @@ namespace Discord.Rest var args = new ModifyGuildIntegrationParams(); func(args); - var model = await Discord.ApiClient.ModifyGuildIntegrationAsync(GuildId, Id, args).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildIntegrationParams + { + EnableEmoticons = args.EnableEmoticons, + ExpireBehavior = args.ExpireBehavior, + ExpireGracePeriod = args.ExpireGracePeriod + }; + var model = await Discord.ApiClient.ModifyGuildIntegrationAsync(GuildId, Id, apiArgs).ConfigureAwait(false); Update(model); } diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index 0b102098c..bea2547e9 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -18,7 +18,15 @@ namespace Discord.Rest { var args = new ModifyGuildRoleParams(); func(args); - return await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildRoleParams + { + Color = args.Color, + Hoist = args.Hoist, + Name = args.Name, + Permissions = args.Permissions, + Position = args.Position + }; + return await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index 545703f0d..4499e1ec1 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Model = Discord.API.User; +using ImageModel = Discord.API.Image; namespace Discord.Rest { @@ -12,14 +13,27 @@ namespace Discord.Rest { var args = new ModifyCurrentUserParams(); func(args); - return await client.ApiClient.ModifySelfAsync(args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyCurrentUserParams + { + Avatar = args.Avatar.IsSpecified ? ImageModel.Create(args.Avatar.Value) : Optional.Create(), + Username = args.Username + }; + return await client.ApiClient.ModifySelfAsync(apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action func, RequestOptions options) { var args = new ModifyGuildMemberParams(); func(args); - await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, args, options).ConfigureAwait(false); + var apiArgs = new API.Rest.ModifyGuildMemberParams + { + ChannelId = args.ChannelId, + Deaf = args.Deaf, + Mute = args.Mute, + Nickname = args.Nickname, + RoleIds = args.RoleIds + }; + await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, apiArgs, options).ConfigureAwait(false); return args; } From e2aa0e92a4a9d1ea5ac3354ad94db2d8d5c9f5ec Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 4 Dec 2016 17:35:36 -0500 Subject: [PATCH 068/263] Slight modification to fix images in thumbnails --- src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index a6c9cfb8c..767641f0e 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -110,7 +110,7 @@ namespace Discord _model.Footer = Footer?.ToModel(); _model.Timestamp = Timestamp?.ToUniversalTime(); _model.Thumbnail = ThumbnailUrl != null ? new Thumbnail { Url = ThumbnailUrl } : null; - _model.Image = ImageUrl != null ? new Image { Url = ImageUrl } : null; + _model.Image = ImageUrl != null ? new ImageEmbed { Url = ImageUrl } : null; _model.Fields = _fields.ToArray(); return _model; } From a4e95923b66e7fdee008ebe44832b1394208f6f3 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 4 Dec 2016 17:51:57 -0500 Subject: [PATCH 069/263] Bitrate must be at least 8000 per the following API error: { "bitrate": [ "Int value should be greater than 8000." ] } --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index 167be4397..00253a3eb 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -357,7 +357,7 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args.Bitrate, 0, nameof(args.Bitrate)); + Preconditions.GreaterThan(args.Bitrate, 8000, nameof(args.Bitrate)); Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); From 3fc043132b62328638324f279061add28a32c9f9 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 4 Dec 2016 19:07:07 -0500 Subject: [PATCH 070/263] docstrings for modify params, minor bugfixes --- .../Channels/ModifyGuildChannelParams.cs | 22 +++++++++ .../Channels/ModifyTextChannelParams.cs | 4 ++ .../Channels/ModifyVoiceChannelParams.cs | 7 +++ .../Entities/Guilds/ModifyGuildEmbedParams.cs | 9 ++++ .../Entities/Guilds/ModifyGuildParams.cs | 43 +++++++++++++++++ src/Discord.Net.Core/Entities/Image.cs | 15 ++++++ .../Entities/Messages/ModifyMessageParams.cs | 35 ++++++++++++-- .../Entities/Roles/ModifyGuildRoleParams.cs | 44 ++++++++++++++++- .../Entities/Users/ModifyCurrentUserParams.cs | 18 +++++++ .../Entities/Users/ModifyGuildMemberParams.cs | 47 ++++++++++++++++++- src/Discord.Net.Core/Utils/Preconditions.cs | 1 + .../Entities/Guilds/GuildHelper.cs | 13 +++-- .../Entities/Roles/RoleHelper.cs | 4 +- .../Entities/Users/UserHelper.cs | 5 +- 14 files changed, 251 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs index 2f1cce4b1..005460915 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs @@ -1,8 +1,30 @@ namespace Discord { + /// + /// Modify an IGuildChannel with the specified changes. + /// + /// + /// + /// await (Context.Channel as ITextChannel)?.ModifyAsync(x => + /// { + /// x.Name = "do-not-enter"; + /// }); + /// + /// public class ModifyGuildChannelParams { + /// + /// Set the channel to this name + /// + /// + /// When modifying an ITextChannel, the Name MUST be alphanumeric with dashes. + /// It must match the following RegEx: [a-z0-9-_]{2,100} + /// + /// A BadRequest will be thrown if the name does not match the above RegEx. public Optional Name { get; set; } + /// + /// Move the channel to the following position. This is 0-based! + /// public Optional Position { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs index fc5c2aad4..d144706d9 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs @@ -1,7 +1,11 @@ namespace Discord { + /// public class ModifyTextChannelParams : ModifyGuildChannelParams { + /// + /// What the topic of the channel should be set to. + /// public Optional Topic { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs index 36941e03b..bb3d2fee5 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs @@ -1,8 +1,15 @@ namespace Discord { + /// public class ModifyVoiceChannelParams : ModifyGuildChannelParams { + /// + /// The bitrate of the voice connections in this channel. Must be greater than 8000 + /// public Optional Bitrate { get; set; } + /// + /// The maximum number of users that can be present in a channel. + /// public Optional UserLimit { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs index 83a32a79c..11b72774c 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs @@ -1,8 +1,17 @@ namespace Discord { + /// + /// Modify the widget of an IGuild with the specified parameters + /// public class ModifyGuildEmbedParams { + /// + /// Should the widget be enabled? + /// public Optional Enabled { get; set; } + /// + /// What channel should the invite place users in, if not null. + /// public Optional ChannelId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs index 19f2476f2..08deff727 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs @@ -1,16 +1,59 @@ namespace Discord { + /// + /// Modify an IGuild with the specified changes + /// + /// + /// + /// await Context.Guild.ModifyAsync(async x => + /// { + /// x.Name = "aaaaaah"; + /// x.RegionId = (await Context.Client.GetOptimalVoiceRegionAsync()).Id; + /// }); + /// + /// + /// public class ModifyGuildParams { public Optional Username { get; set; } + /// + /// The name of the Guild + /// public Optional Name { get; set; } + /// + /// The ID of the region for the Guild's voice connections + /// public Optional RegionId { get; set; } + /// + /// What verification level new users need to achieve before speaking + /// public Optional VerificationLevel { get; set; } + /// + /// The default message notification state for the guild + /// public Optional DefaultMessageNotifications { get; set; } + /// + /// How many seconds before a user is sent to AFK. This value MUST be one of: (60, 300, 900, 1800, 3600). + /// public Optional AfkTimeout { get; set; } + /// + /// The icon of the guild + /// public Optional Icon { get; set; } + /// + /// The guild's splash image + /// + /// + /// The guild must be partnered for this value to have any effect. + /// public Optional Splash { get; set; } + /// + /// The ID of the IVoiceChannel where AFK users should be sent. + /// public Optional AfkChannelId { get; set; } + /// + /// The ID of the owner of this guild. + /// public Optional OwnerId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index bb305f113..538d82730 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -2,13 +2,28 @@ namespace Discord { + /// + /// An image that will be uploaded to Discord. + /// public struct Image { public Stream Stream { get; } + /// + /// Create the image with a Stream. + /// + /// This must be some type of stream with the contents of a file in it. + /// public Image(Stream stream) { Stream = stream; } + /// + /// Create the image from a file path. + /// + /// + /// This file path is NOT validated, and is passed directly into a + /// + /// The path to the file. public Image(string path) { Stream = File.OpenRead(path); diff --git a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs index cc05e620a..6b7e3b478 100644 --- a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs +++ b/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs @@ -1,12 +1,37 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Discord +namespace Discord { + /// + /// Modify a message with the specified parameters. + /// + /// + /// The content of a message can be cleared with String.Empty; if and only if an Embed is present. + /// + /// + /// + /// var message = await ReplyAsync("abc"); + /// await message.ModifyAsync(x => + /// { + /// x.Content = ""; + /// x.Embed = new EmbedBuilder() + /// .WithColor(new Color(40, 40, 120)) + /// .WithAuthor(a => a.Name = "foxbot") + /// .WithTitle("Embed!") + /// .WithDescription("This is an embed."); + /// }); + /// + /// public class ModifyMessageParams { + /// + /// The content of the message + /// + /// + /// This must be less than 2000 characters. + /// public Optional Content { get; set; } + /// + /// The embed the message should display + /// public Optional Embed { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs index 486d56513..fa99c5bcc 100644 --- a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs @@ -1,11 +1,51 @@ namespace Discord { + /// + /// Modify an IRole with the specified parameters + /// + /// + /// + /// await role.ModifyAsync(x => + /// { + /// x.Color = new Color(180, 15, 40); + /// x.Hoist = true; + /// }); + /// + /// + /// public class ModifyGuildRoleParams { + /// + /// The name of the role + /// + /// + /// If this role is the EveryoneRole, this value may not be set. + /// public Optional Name { get; set; } - public Optional Permissions { get; set; } + /// + /// The role's GuildPermissions + /// + public Optional Permissions { get; set; } + /// + /// The position of the role. This is 0-based! + /// + /// + /// If this role is the EveryoneRole, this value may not be set. + /// public Optional Position { get; set; } - public Optional Color { get; set; } + /// + /// The color of the Role. + /// + /// + /// If this role is the EveryoneRole, this value may not be set. + /// + public Optional Color { get; set; } + /// + /// Whether or not this role should be displayed independently in the userlist. + /// + /// + /// If this role is the EveryoneRole, this value may not be set. + /// public Optional Hoist { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs index cdd031c34..c517b88df 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs @@ -1,8 +1,26 @@ namespace Discord { + /// + /// Modify the current user with the specified arguments + /// + /// + /// + /// await Context.Client.CurrentUser.ModifyAsync(x => + /// { + /// x.Avatar = new Image(File.OpenRead("avatar.jpg")); + /// }); + /// + /// + /// public class ModifyCurrentUserParams { + /// + /// Your username + /// public Optional Username { get; set; } + /// + /// Your avatar + /// public Optional Avatar { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs index 8e74d80e0..57909cd03 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs +++ b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs @@ -1,11 +1,54 @@ namespace Discord { + /// + /// Modify an IGuildUser with the following parameters. + /// + /// + /// + /// await (Context.User as IGuildUser)?.ModifyAsync(x => + /// { + /// x.Nickname = $"festive {Context.User.Username}"; + /// }); + /// + /// + /// public class ModifyGuildMemberParams { + /// + /// Should the user be guild-muted in a voice channel? + /// + /// + /// If this value is set to true, no user will be able to hear this user speak in the guild. + /// public Optional Mute { get; set; } + /// + /// Should the user be guild-deafened in a voice channel? + /// + /// + /// If this value is set to true, this user will not be able to hear anyone speak in the guild. + /// public Optional Deaf { get; set; } + /// + /// Should the user have a nickname set? + /// + /// + /// To clear the user's nickname, this value can be set to null. + /// public Optional Nickname { get; set; } - public Optional RoleIds { get; set; } - public Optional ChannelId { get; set; } + /// + /// What roles should the user have? + /// + /// + /// To add a role to a user: + /// To remove a role from a user: + /// + public Optional Roles { get; set; } + /// + /// Move a user to a voice channel. + /// + /// + /// This user MUST already be in a Voice Channel for this to work. + /// + public Optional Channel { get; set; } } } diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 14a730b7e..02cc5d288 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; namespace Discord { diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index fb3e8e2cf..428541509 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -64,7 +64,14 @@ namespace Discord.Rest public static async Task> ModifyRolesAsync(IGuild guild, BaseDiscordClient client, IEnumerable args, RequestOptions options) { - var apiArgs = args.Select(x => new API.Rest.ModifyGuildRolesParams(x.Id) { Color = x.Color, Hoist = x.Hoist, Name = x.Name, Permissions = x.Permissions, Position = x.Position }); + var apiArgs = args.Select(x => new API.Rest.ModifyGuildRolesParams(x.Id) + { + Color = x.Color.IsSpecified ? x.Color.Value.RawValue : Optional.Create(), + Hoist = x.Hoist, + Name = x.Name, + Permissions = x.Permissions.IsSpecified ? x.Permissions.Value.RawValue : Optional.Create(), + Position = x.Position + }); return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client, @@ -167,8 +174,8 @@ namespace Discord.Rest await role.ModifyAsync(x => { x.Name = name; - x.Permissions = (permissions ?? role.Permissions).RawValue; - x.Color = (color ?? Color.Default).RawValue; + x.Permissions = (permissions ?? role.Permissions); + x.Color = (color ?? Color.Default); x.Hoist = isHoisted; }, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index bea2547e9..4b786b6e6 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -20,10 +20,10 @@ namespace Discord.Rest func(args); var apiArgs = new API.Rest.ModifyGuildRoleParams { - Color = args.Color, + Color = args.Color.IsSpecified ? args.Color.Value.RawValue : Optional.Create(), Hoist = args.Hoist, Name = args.Name, - Permissions = args.Permissions, + Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue : Optional.Create(), Position = args.Position }; return await client.ApiClient.ModifyGuildRoleAsync(role.Guild.Id, role.Id, apiArgs, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index 4499e1ec1..dbfa2f2e5 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; using Model = Discord.API.User; using ImageModel = Discord.API.Image; +using System.Linq; namespace Discord.Rest { @@ -27,11 +28,11 @@ namespace Discord.Rest func(args); var apiArgs = new API.Rest.ModifyGuildMemberParams { - ChannelId = args.ChannelId, + ChannelId = args.Channel.IsSpecified ? args.Channel.Value.Id : Optional.Create(), Deaf = args.Deaf, Mute = args.Mute, Nickname = args.Nickname, - RoleIds = args.RoleIds + RoleIds = args.Roles.IsSpecified ? args.Roles.Value.Select(r => r.Id).ToArray() : Optional.Create(), }; await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, apiArgs, options).ConfigureAwait(false); return args; From ec71b84720023ae4a34ef7b24760f960ca896595 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 4 Dec 2016 19:47:15 -0500 Subject: [PATCH 071/263] Add config option to download all users on GuildAvailable Resolves #302 --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 11 +++++++++++ src/Discord.Net.WebSocket/DiscordSocketConfig.cs | 3 +++ 2 files changed, 14 insertions(+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 5776d8470..020a40967 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -57,6 +57,7 @@ namespace Discord.WebSocket internal ClientState State { get; private set; } internal int ConnectionTimeout { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; } + internal bool DownloadUsersOnGuildAvailable { get; private set; } public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } @@ -76,6 +77,7 @@ namespace Discord.WebSocket LargeThreshold = config.LargeThreshold; AudioMode = config.AudioMode; WebSocketProvider = config.WebSocketProvider; + DownloadUsersOnGuildAvailable = config.DownloadUsersOnGuildAvailable; ConnectionTimeout = config.ConnectionTimeout; State = new ClientState(0, 0); @@ -108,6 +110,15 @@ namespace Discord.WebSocket GuildUnavailable += async g => await _gatewayLogger.VerboseAsync($"Disconnected from {g.Name}").ConfigureAwait(false); LatencyUpdated += async (old, val) => await _gatewayLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); + if (DownloadUsersOnGuildAvailable) + { + GuildAvailable += g => + { + var _ = g.DownloadUsersAsync(); + return Task.CompletedTask; + }; + } + _voiceRegions = ImmutableDictionary.Create(); _largeGuilds = new ConcurrentQueue(); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index dc0347a1c..9bf337f8e 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -28,5 +28,8 @@ namespace Discord.WebSocket /// Gets or sets the provider used to generate new websocket connections. public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); + + /// Gets or sets whether or not all users should be downloaded as guilds come available. + public bool DownloadUsersOnGuildAvailable { get; set; } = false; } } From 5e965ea32588beb353ba56caf5a8f4bde95a5c04 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 5 Dec 2016 07:09:53 -0400 Subject: [PATCH 072/263] Allow tokenless RPC connections --- src/Discord.Net.Rpc/DiscordRpcClient.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 3ca288b12..9efa2d312 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -70,21 +70,17 @@ namespace Discord.Rpc => new API.DiscordRpcApiClient(clientId, DiscordRestConfig.UserAgent, origin, config.RestClientProvider, config.WebSocketProvider, requestQueue: new RequestQueue()); /// - public Task ConnectAsync() => ConnectAsync(false); - internal async Task ConnectAsync(bool ignoreLoginCheck) + public async Task ConnectAsync() { await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await ConnectInternalAsync(ignoreLoginCheck, false).ConfigureAwait(false); + await ConnectInternalAsync(false).ConfigureAwait(false); } finally { _connectionLock.Release(); } } - private async Task ConnectInternalAsync(bool ignoreLoginCheck, bool isReconnecting) - { - if (!ignoreLoginCheck && LoginState != LoginState.LoggedIn) - throw new InvalidOperationException("You must log in before connecting."); - + private async Task ConnectInternalAsync(bool isReconnecting) + { if (!isReconnecting && _reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) _reconnectCancelToken.Cancel(); @@ -198,7 +194,7 @@ namespace Discord.Rpc try { if (cancelToken.IsCancellationRequested) return; - await ConnectInternalAsync(false, true).ConfigureAwait(false); + await ConnectInternalAsync(true).ConfigureAwait(false); _reconnectTask = null; return; } @@ -223,7 +219,7 @@ namespace Discord.Rpc public async Task AuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) { - await ConnectAsync(true).ConfigureAwait(false); + await ConnectAsync().ConfigureAwait(false); var result = await ApiClient.SendAuthorizeAsync(scopes, rpcToken, options).ConfigureAwait(false); await DisconnectAsync().ConfigureAwait(false); return result.Code; From c83e6939882164f9a9aa7888eb7710d87ee6936f Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 5 Dec 2016 18:58:35 -0400 Subject: [PATCH 073/263] Removed duplicate logic --- src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 53475e5a2..3ef45d230 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -88,7 +88,6 @@ namespace Discord.WebSocket internal override void Update(ClientState state, PresenceModel model) { base.Update(state, model); - GlobalUser.Update(state, model.User); if (model.Roles.IsSpecified) UpdateRoles(model.Roles.Value); if (model.Nick.IsSpecified) From 70d30efb949baf6bf9ea28fef94a929175b447cb Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Fri, 9 Dec 2016 10:31:57 +0000 Subject: [PATCH 074/263] Implement configurable command node separators This fixes #304 over foxbot's PR for the same issue. --- src/Discord.Net.Commands/CommandService.cs | 9 ++- .../CommandServiceConfig.cs | 2 + src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- src/Discord.Net.Commands/Info/ModuleInfo.cs | 8 +-- src/Discord.Net.Commands/Map/CommandMap.cs | 12 ++-- .../Map/CommandMapNode.cs | 64 +++++++++++++------ 6 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 9c2440b7c..b2be1e637 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -22,6 +22,7 @@ namespace Discord.Commands internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; + internal readonly char _splitCharacter; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -74,6 +75,8 @@ namespace Discord.Commands }; _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; + + _splitCharacter = config.CommandSplitCharacter; } //Modules @@ -143,7 +146,7 @@ namespace Discord.Commands _moduleDefs.Add(module); foreach (var command in module.Commands) - _map.AddCommand(command); + _map.AddCommand(command, this); foreach (var submodule in module.Submodules) LoadModuleInternal(submodule); @@ -187,7 +190,7 @@ namespace Discord.Commands return false; foreach (var cmd in module.Commands) - _map.RemoveCommand(cmd); + _map.RemoveCommand(cmd, this); foreach (var submodule in module.Submodules) { @@ -228,7 +231,7 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); - var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Priority).ToImmutableArray(); + var matches = _map.GetCommands(searchInput, this).OrderByDescending(x => x.Priority).ToImmutableArray(); if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 4ac79fe8f..c73ce65f2 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -6,5 +6,7 @@ public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; /// Should commands be case-sensitive? public bool CaseSensitiveCommands { get; set; } = false; + /// The character which splits commands + public char CommandSplitCharacter { get; set; } = ' '; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a6ac50005..1e41a0aa9 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -44,7 +44,7 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + service._splitCharacter + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // only module provides aliases else if (module.Aliases.Count > 0) Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index ab4f65713..65417e3fd 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -30,14 +30,14 @@ namespace Discord.Commands Remarks = builder.Remarks; Parent = parent; - Aliases = BuildAliases(builder).ToImmutableArray(); + Aliases = BuildAliases(builder, service).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)); Preconditions = BuildPreconditions(builder).ToImmutableArray(); Submodules = BuildSubmodules(builder, service).ToImmutableArray(); } - private static IEnumerable BuildAliases(ModuleBuilder builder) + private static IEnumerable BuildAliases(ModuleBuilder builder, CommandService service) { IEnumerable result = null; @@ -60,9 +60,9 @@ namespace Discord.Commands result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly } else if (result.Count() > level.Aliases.Count) - result = result.Permutate(level.Aliases, (first, second) => first + " " + second); + result = result.Permutate(level.Aliases, (first, second) => first + service._splitCharacter + second); else - result = level.Aliases.Permutate(result, (second, first) => first + " " + second); + result = level.Aliases.Permutate(result, (second, first) => first + service._splitCharacter + second); } if (result == null) //there were no aliases; default to an empty list diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs index 3a5239878..2478f488d 100644 --- a/src/Discord.Net.Commands/Map/CommandMap.cs +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -12,20 +12,20 @@ namespace Discord.Commands _root = new CommandMapNode(""); } - public void AddCommand(CommandInfo command) + public void AddCommand(CommandInfo command, CommandService service) { foreach (string text in GetAliases(command)) - _root.AddCommand(text, 0, command); + _root.AddCommand(service, text, 0, command); } - public void RemoveCommand(CommandInfo command) + public void RemoveCommand(CommandInfo command, CommandService service) { foreach (string text in GetAliases(command)) - _root.RemoveCommand(text, 0, command); + _root.RemoveCommand(service, text, 0, command); } - public IEnumerable GetCommands(string text) + public IEnumerable GetCommands(string text, CommandService service) { - return _root.GetCommands(text, 0); + return _root.GetCommands(service, text, 0); } private IReadOnlyList GetAliases(CommandInfo command) diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs index a86c0643d..c1365f963 100644 --- a/src/Discord.Net.Commands/Map/CommandMapNode.cs +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -23,9 +23,9 @@ namespace Discord.Commands _commands = ImmutableArray.Create(); } - public void AddCommand(string text, int index, CommandInfo command) + public void AddCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSpace = NextWhitespace(service, text, index); string name; lock (_lockObj) @@ -44,13 +44,13 @@ namespace Discord.Commands name = text.Substring(index, nextSpace - index); var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); - nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.AddCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); } } } - public void RemoveCommand(string text, int index, CommandInfo command) + public void RemoveCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSpace = NextWhitespace(service, text, index); string name; lock (_lockObj) @@ -67,7 +67,7 @@ namespace Discord.Commands CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.RemoveCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); if (nextNode.IsEmpty) _nodes.TryRemove(name, out nextNode); } @@ -75,32 +75,57 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(string text, int index) + public IEnumerable GetCommands(CommandService service, string text, int index) { - int nextSpace = NextWhitespace(text, index); - string name; - - var commands = _commands; - for (int i = 0; i < commands.Length; i++) - yield return _commands[i]; + int nextCommand = NextCommandSegment(service, text, index); + string name = null; - if (text != "") + //got all command segments or base-level command + if (nextCommand == -1) { - if (nextSpace == -1) - name = text.Substring(index); - else + var commands = _commands; + for (int i = 0; i < commands.Length; i++) + yield return _commands[i]; + + //are we a base-level command? + int nextSpace = NextWhitespace(service, text, index); + if (nextSpace != -1) + { name = text.Substring(index, nextSpace - index); + } + else + { + name = text.Substring(index); + } + } + else + { + name = text.Substring(index, nextCommand - index); + } + if (name != null) + { CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1)) + foreach (var cmd in nextNode.GetCommands(service, text, nextCommand + 1)) yield return cmd; } } } - private static int NextWhitespace(string text, int startIndex) + private static int NextCommandSegment(CommandService service, string text, int startIndex) + { + int lowest = int.MaxValue; + + int index = text.IndexOf(service._splitCharacter, startIndex); + if (index != -1 && index < lowest) + lowest = index; + + return (lowest != int.MaxValue) ? lowest : -1; + } + + private static int NextWhitespace(CommandService service, string text, int startIndex) { int lowest = int.MaxValue; for (int i = 0; i < _whitespaceChars.Length; i++) @@ -109,6 +134,7 @@ namespace Discord.Commands if (index != -1 && index < lowest) lowest = index; } + return (lowest != int.MaxValue) ? lowest : -1; } } From b4f8b0601f658e43193efa20471e4463cb71cd3e Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 10 Dec 2016 19:05:33 +0000 Subject: [PATCH 075/263] Fix #408 A.K.A. "FiniteReality is a twat" --- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index e8dc60de8..a501dfb66 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -202,7 +202,13 @@ namespace Discord.Commands if (builder.TypeReader == null) { - var reader = service.GetDefaultTypeReader(paramType); + var readers = service.GetTypeReaders(paramType); + TypeReader reader = null; + + if (readers != null) + reader = readers.FirstOrDefault().Value; + else + reader = service.GetDefaultTypeReader(paramType); if (reader == null) { From 9aa924f75cf758662e1d54e444b87f188cd7dbb3 Mon Sep 17 00:00:00 2001 From: Finite Reality Date: Sun, 11 Dec 2016 18:07:30 +0000 Subject: [PATCH 076/263] Remove blank line --- src/Discord.Net.Commands/CommandService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b2be1e637..5f5729673 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -75,7 +75,6 @@ namespace Discord.Commands }; _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; - _splitCharacter = config.CommandSplitCharacter; } From 0f334d24a0f53d5906e54489eefc18f44f0ee94e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 14 Dec 2016 16:12:02 -0500 Subject: [PATCH 077/263] Add Transients/Factories to Dependency Injection --- .../Dependencies/DependencyMap.cs | 25 +++++++++++++++---- .../Dependencies/IDependencyMap.cs | 5 +++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs index ba5995f26..7f489fda0 100644 --- a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs @@ -5,21 +5,29 @@ namespace Discord.Commands { public class DependencyMap : IDependencyMap { - private Dictionary map; + private Dictionary> map; public static DependencyMap Empty => new DependencyMap(); public DependencyMap() { - map = new Dictionary(); + map = new Dictionary>(); } - public void Add(T obj) + public void Add(T obj) where T : class + => AddFactory(() => obj); + public void AddTransient() where T : class, new() + => AddFactory(() => new T()); + public void AddTransient() where TKey : class + where TImpl : class, TKey, new() + => AddFactory(() => new TImpl()); + + public void AddFactory(Func factory) where T : class { var t = typeof(T); if (map.ContainsKey(t)) throw new InvalidOperationException($"The dependency map already contains \"{t.FullName}\""); - map.Add(t, obj); + map.Add(t, factory); } public T Get() @@ -51,7 +59,14 @@ namespace Discord.Commands } public bool TryGet(Type t, out object result) { - return map.TryGetValue(t, out result); + Func func; + if (map.TryGetValue(t, out func)) + { + result = func(); + return true; + } + result = null; + return false; } } } diff --git a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs index 784a9bc56..aab94156b 100644 --- a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs @@ -4,7 +4,10 @@ namespace Discord.Commands { public interface IDependencyMap { - void Add(T obj); + void Add(T obj) where T : class; + void AddTransient() where T : class, new(); + void AddTransient() where TKey: class where TImpl : class, TKey, new(); + void AddFactory(Func factory) where T : class; T Get(); bool TryGet(out T result); From b33df6ad7712d0330d3bb93b7d75bd6f0db05993 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 14 Dec 2016 16:38:28 -0500 Subject: [PATCH 078/263] Add InjectAttribute, inject into fields flagged with it from DepMap This allows users to flag a field with InjectAttribute, and when the module is created at runtime, this field will be filled in with the object from the dependency map. --- .../Attributes/InjectAttribute.cs | 9 +++++++++ .../Utilities/ReflectionUtils.cs | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/Discord.Net.Commands/Attributes/InjectAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs new file mode 100644 index 000000000..1ad1d4ee3 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Discord.Commands +{ + [AttributeUsage(AttributeTargets.Field)] + public class InjectAttribute : Attribute + { + } +} diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 27ea601bf..b7320ba5a 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -42,7 +42,23 @@ namespace Discord.Commands try { - return (T)constructor.Invoke(args); + T type = (T)constructor.Invoke(args); + var fields = type.GetType().GetRuntimeFields().Where(p => p.GetCustomAttribute() != null); + foreach (var field in fields) + { + object arg; + if (map == null || !map.TryGet(field.FieldType, out arg)) + { + if (field.FieldType == typeof(CommandService)) + arg = service; + else if (field.FieldType == typeof(IDependencyMap)) + arg = map; + else + throw new InvalidOperationException($"Failed to inject \"{typeInfo.FullName}\", dependency \"{field.FieldType.FullName}\" was not found."); + } + field.SetValue(type, arg); + } + return type; } catch (Exception ex) { From 7fb032c9d2230d02ad3a88b0020f8e5cbe0a39af Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 14 Dec 2016 17:07:24 -0500 Subject: [PATCH 079/263] Make changes per discussion Instead of using fields, we will now use properties (that must have a setter). --- .../Attributes/InjectAttribute.cs | 4 ++-- .../Utilities/ReflectionUtils.cs | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs index 1ad1d4ee3..836a4c668 100644 --- a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs @@ -2,8 +2,8 @@ namespace Discord.Commands { - [AttributeUsage(AttributeTargets.Field)] - public class InjectAttribute : Attribute + [AttributeUsage(AttributeTargets.Property)] + public sealed class InjectAttribute : Attribute { } } diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index b7320ba5a..0bcb24882 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -42,23 +42,23 @@ namespace Discord.Commands try { - T type = (T)constructor.Invoke(args); - var fields = type.GetType().GetRuntimeFields().Where(p => p.GetCustomAttribute() != null); + T instance = (T)constructor.Invoke(args); + var fields = instance.GetType().GetRuntimeProperties().Where(p => p.GetCustomAttribute() != null).Where(p => p.CanWrite); foreach (var field in fields) { object arg; - if (map == null || !map.TryGet(field.FieldType, out arg)) + if (map == null || !map.TryGet(field.PropertyType, out arg)) { - if (field.FieldType == typeof(CommandService)) + if (field.PropertyType == typeof(CommandService)) arg = service; - else if (field.FieldType == typeof(IDependencyMap)) + else if (field.PropertyType == typeof(IDependencyMap)) arg = map; else - throw new InvalidOperationException($"Failed to inject \"{typeInfo.FullName}\", dependency \"{field.FieldType.FullName}\" was not found."); + throw new InvalidOperationException($"Failed to inject \"{typeInfo.FullName}\", dependency \"{field.PropertyType.FullName}\" was not found."); } - field.SetValue(type, arg); + field.SetValue(instance, arg); } - return type; + return instance; } catch (Exception ex) { From 8f87b2cc7142f002e2270809dfadc9b4b208feed Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 05:51:07 -0400 Subject: [PATCH 080/263] Added support for .NET Standard 1.1 and 1.2 --- README.md | 2 +- .../Builders/ModuleClassBuilder.cs | 21 +-- .../Discord.Net.Commands.csproj | 20 +-- src/Discord.Net.Commands/project.json | 9 +- .../API/DiscordRestApiClient.cs | 4 +- src/Discord.Net.Core/Discord.Net.Core.csproj | 46 ++----- .../Entities/Channels/IMessageChannel.cs | 2 + .../Extensions/CollectionExtensions.cs | 20 ++- src/Discord.Net.Core/Logging/LogManager.cs | 42 ++++-- src/Discord.Net.Core/Logging/Logger.cs | 32 +++-- .../Net/Queue/RequestQueueBucket.cs | 2 +- src/Discord.Net.Core/Net/RateLimitInfo.cs | 2 +- src/Discord.Net.Core/Net/Udp/IUdpSocket.cs | 19 +++ .../Net/Udp/UdpSocketProvider.cs | 4 + src/Discord.Net.Core/Utils/DateTimeUtils.cs | 62 +++++++-- src/Discord.Net.Core/project.json | 15 +- src/Discord.Net.Rest/BaseDiscordClient.cs | 4 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 24 +--- src/Discord.Net.Rest/DiscordRestClient.cs | 4 +- .../Entities/Channels/ChannelHelper.cs | 2 + .../Entities/Channels/IRestMessageChannel.cs | 2 + .../Entities/Channels/RestDMChannel.cs | 4 + .../Entities/Channels/RestGroupChannel.cs | 4 + .../Entities/Channels/RestTextChannel.cs | 4 + .../Channels/RestVirtualMessageChannel.cs | 4 + .../Entities/Messages/MessageHelper.cs | 16 +-- .../Net}/DefaultRestClient.cs | 2 +- src/Discord.Net.Rest/project.json | 10 +- .../API/DiscordRpcApiClient.cs | 3 - src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 36 ++--- src/Discord.Net.Rpc/DiscordRpcConfig.cs | 16 ++- .../Entities/Channels/RpcDMChannel.cs | 4 + .../Entities/Channels/RpcGroupChannel.cs | 4 + .../Entities/Channels/RpcTextChannel.cs | 4 + src/Discord.Net.Rpc/project.json | 12 +- .../API/DiscordVoiceApiClient.cs | 60 ++++---- .../Audio/AudioClient.cs | 8 +- .../Discord.Net.WebSocket.csproj | 40 ++---- .../DiscordSocketClient.cs | 7 +- .../DiscordSocketConfig.cs | 25 +++- .../Channels/ISocketMessageChannel.cs | 2 + .../Entities/Channels/SocketDMChannel.cs | 4 + .../Entities/Channels/SocketGroupChannel.cs | 4 + .../Entities/Channels/SocketTextChannel.cs | 4 + .../Net/DefaultUdpSocket.cs | 129 ++++++++++++++++++ .../Net}/DefaultWebSocketClient.cs | 42 ++++-- src/Discord.Net.WebSocket/project.json | 14 +- src/Discord.Net/Discord.Net.csproj | 22 +-- src/Discord.Net/project.json | 9 +- 49 files changed, 508 insertions(+), 323 deletions(-) create mode 100644 src/Discord.Net.Core/Net/Udp/IUdpSocket.cs create mode 100644 src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs rename src/{Discord.Net.Core/Net/Rest => Discord.Net.Rest/Net}/DefaultRestClient.cs (99%) create mode 100644 src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs rename src/{Discord.Net.Core/Net/WebSockets => Discord.Net.WebSocket/Net}/DefaultWebSocketClient.cs (87%) diff --git a/README.md b/README.md index 268a6d81d..b287d7015 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Bleeding edge builds are available using our MyGet feed (`https://www.myget.org/ In order to compile Discord.Net, you require the following: ### Using Visual Studio -- [Visual Studio 2017 RC](https://www.microsoft.com/net/core#windowsvs2017) +- [Visual Studio 2017 RC Build 26014.0](https://www.microsoft.com/net/core#windowsvs2017) The .NET Core and Docker (Preview) workload is required during Visual Studio installation. diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index a501dfb66..776b799c9 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -28,8 +28,8 @@ namespace Discord.Commands public static Dictionary Build(CommandService service, params TypeInfo[] validTypes) => Build(validTypes, service); public static Dictionary Build(IEnumerable validTypes, CommandService service) { - if (!validTypes.Any()) - throw new InvalidOperationException("Could not find any valid modules from the given selection"); + /*if (!validTypes.Any()) + throw new InvalidOperationException("Could not find any valid modules from the given selection");*/ var topLevelGroups = validTypes.Where(x => x.DeclaringType == null); var subGroups = validTypes.Intersect(topLevelGroups); @@ -65,7 +65,8 @@ namespace Discord.Commands if (builtTypes.Contains(typeInfo)) continue; - builder.AddModule((module) => { + builder.AddModule((module) => + { BuildModule(module, typeInfo, service); BuildSubTypes(module, typeInfo.DeclaredNestedTypes, builtTypes, service); }); @@ -106,7 +107,8 @@ namespace Discord.Commands foreach (var method in validCommands) { - builder.AddCommand((command) => { + builder.AddCommand((command) => + { BuildCommand(command, typeInfo, method, service); }); } @@ -147,21 +149,24 @@ namespace Discord.Commands int pos = 0, count = parameters.Length; foreach (var paramInfo in parameters) { - builder.AddParameter((parameter) => { + builder.AddParameter((parameter) => + { BuildParameter(parameter, paramInfo, pos++, count, service); }); } var createInstance = ReflectionUtils.CreateBuilder(typeInfo, service); - builder.Callback = (ctx, args, map) => { + builder.Callback = (ctx, args, map) => + { var instance = createInstance(map); instance.Context = ctx; try { - return method.Invoke(instance, args) as Task ?? Task.CompletedTask; + return method.Invoke(instance, args) as Task ?? Task.Delay(0); } - finally{ + finally + { (instance as IDisposable)?.Dispose(); } }; diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 63ffafd2c..9e993b624 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,41 +1,25 @@ - - + A Discord.Net extension adding support for bot commands. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - - 1.0.0-alpha-20161104-2 - All - - - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Commands/project.json b/src/Discord.Net.Commands/project.json index a87591c03..d918d0e7e 100644 --- a/src/Discord.Net.Commands/project.json +++ b/src/Discord.Net.Commands/project.json @@ -32,12 +32,7 @@ }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } \ No newline at end of file diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index bc7436cd4..0d0b7914b 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -160,8 +160,8 @@ namespace Discord.API LoginState = LoginState.LoggedOut; } - internal virtual Task ConnectInternalAsync() => Task.CompletedTask; - internal virtual Task DisconnectInternalAsync() => Task.CompletedTask; + internal virtual Task ConnectInternalAsync() => Task.Delay(0); + internal virtual Task DisconnectInternalAsync() => Task.Delay(0); //Core internal Task SendAsync(string method, Expression> endpointExpr, BucketIds ids, diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index b2d9fc870..c64ed88c2 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,60 +1,30 @@ - - + A .Net API wrapper and bot framework for Discord. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 9.0.1 - - - 4.3.0 - - - 1.3.0 - - - 3.1.0 - - - 4.3.0 - - - 4.3.0 - All - + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 41bc79511..389a2cc27 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -9,8 +9,10 @@ namespace Discord { /// Sends a message to this message channel. Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Extensions/CollectionExtensions.cs b/src/Discord.Net.Core/Extensions/CollectionExtensions.cs index 8eebac817..e5d6025c2 100644 --- a/src/Discord.Net.Core/Extensions/CollectionExtensions.cs +++ b/src/Discord.Net.Core/Extensions/CollectionExtensions.cs @@ -8,24 +8,30 @@ namespace Discord { internal static class CollectionExtensions { - public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyDictionary source) - => new ConcurrentDictionaryWrapper(source.Select(x => x.Value), () => source.Count); + //public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyCollection source) + // => new CollectionWrapper(source, () => source.Count); + public static IReadOnlyCollection ToReadOnlyCollection(this ICollection source) + => new CollectionWrapper(source, () => source.Count); + //public static IReadOnlyCollection ToReadOnlyCollection(this IReadOnlyDictionary source) + // => new CollectionWrapper(source.Select(x => x.Value), () => source.Count); + public static IReadOnlyCollection ToReadOnlyCollection(this IDictionary source) + => new CollectionWrapper(source.Select(x => x.Value), () => source.Count); public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable query, IReadOnlyCollection source) - => new ConcurrentDictionaryWrapper(query, () => source.Count); + => new CollectionWrapper(query, () => source.Count); public static IReadOnlyCollection ToReadOnlyCollection(this IEnumerable query, Func countFunc) - => new ConcurrentDictionaryWrapper(query, countFunc); + => new CollectionWrapper(query, countFunc); } [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal struct ConcurrentDictionaryWrapper : IReadOnlyCollection + internal struct CollectionWrapper : IReadOnlyCollection { private readonly IEnumerable _query; private readonly Func _countFunc; //It's okay that this count is affected by race conditions - we're wrapping a concurrent collection and that's to be expected public int Count => _countFunc(); - - public ConcurrentDictionaryWrapper(IEnumerable query, Func countFunc) + + public CollectionWrapper(IEnumerable query, Func countFunc) { _query = query; _countFunc = countFunc; diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 104e02835..0a2fce738 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -17,56 +17,68 @@ namespace Discord.Logging ClientLogger = new Logger(this, "Discord"); } + public async Task LogAsync(LogSeverity severity, string source, Exception ex) + { + if (severity <= Level) + await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); + } public async Task LogAsync(LogSeverity severity, string source, string message, Exception ex = null) { if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, message, ex)).ConfigureAwait(false); } +#if NETSTANDARD1_3 public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) { if (severity <= Level) await _messageEvent.InvokeAsync(new LogMessage(severity, source, message.ToString(), ex)).ConfigureAwait(false); } - public async Task LogAsync(LogSeverity severity, string source, Exception ex) - { - if (severity <= Level) - await _messageEvent.InvokeAsync(new LogMessage(severity, source, null, ex)).ConfigureAwait(false); - } +#endif + public Task ErrorAsync(string source, Exception ex) + => LogAsync(LogSeverity.Error, source, ex); public Task ErrorAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); +#if NETSTANDARD1_3 public Task ErrorAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, ex); - public Task ErrorAsync(string source, Exception ex) - => LogAsync(LogSeverity.Error, source, ex); +#endif + public Task WarningAsync(string source, Exception ex) + => LogAsync(LogSeverity.Warning, source, ex); public Task WarningAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); +#if NETSTANDARD1_3 public Task WarningAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, ex); - public Task WarningAsync(string source, Exception ex) - => LogAsync(LogSeverity.Warning, source, ex); +#endif + public Task InfoAsync(string source, Exception ex) + => LogAsync(LogSeverity.Info, source, ex); public Task InfoAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); +#if NETSTANDARD1_3 public Task InfoAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, ex); - public Task InfoAsync(string source, Exception ex) - => LogAsync(LogSeverity.Info, source, ex); +#endif + public Task VerboseAsync(string source, Exception ex) + => LogAsync(LogSeverity.Verbose, source, ex); public Task VerboseAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); +#if NETSTANDARD1_3 public Task VerboseAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, ex); - public Task VerboseAsync(string source, Exception ex) - => LogAsync(LogSeverity.Verbose, source, ex); +#endif + public Task DebugAsync(string source, Exception ex) + => LogAsync(LogSeverity.Debug, source, ex); public Task DebugAsync(string source, string message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); +#if NETSTANDARD1_3 public Task DebugAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, ex); - public Task DebugAsync(string source, Exception ex) - => LogAsync(LogSeverity.Debug, source, ex); +#endif public Logger CreateLogger(string name) => new Logger(this, name); diff --git a/src/Discord.Net.Core/Logging/Logger.cs b/src/Discord.Net.Core/Logging/Logger.cs index c871c0b26..cff69a84c 100644 --- a/src/Discord.Net.Core/Logging/Logger.cs +++ b/src/Discord.Net.Core/Logging/Logger.cs @@ -20,42 +20,54 @@ namespace Discord.Logging => _manager.LogAsync(severity, Name, exception); public Task LogAsync(LogSeverity severity, string message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); +#if NETSTANDARD1_3 public Task LogAsync(LogSeverity severity, FormattableString message, Exception exception = null) => _manager.LogAsync(severity, Name, message, exception); +#endif + public Task ErrorAsync(Exception exception) + => _manager.ErrorAsync(Name, exception); public Task ErrorAsync(string message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task ErrorAsync(FormattableString message, Exception exception = null) => _manager.ErrorAsync(Name, message, exception); - public Task ErrorAsync(Exception exception) - => _manager.ErrorAsync(Name, exception); +#endif + public Task WarningAsync(Exception exception) + => _manager.WarningAsync(Name, exception); public Task WarningAsync(string message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task WarningAsync(FormattableString message, Exception exception = null) => _manager.WarningAsync(Name, message, exception); - public Task WarningAsync(Exception exception) - => _manager.WarningAsync(Name, exception); +#endif + public Task InfoAsync(Exception exception) + => _manager.InfoAsync(Name, exception); public Task InfoAsync(string message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task InfoAsync(FormattableString message, Exception exception = null) => _manager.InfoAsync(Name, message, exception); - public Task InfoAsync(Exception exception) - => _manager.InfoAsync(Name, exception); +#endif + public Task VerboseAsync(Exception exception) + => _manager.VerboseAsync(Name, exception); public Task VerboseAsync(string message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task VerboseAsync(FormattableString message, Exception exception = null) => _manager.VerboseAsync(Name, message, exception); - public Task VerboseAsync(Exception exception) - => _manager.VerboseAsync(Name, exception); +#endif + public Task DebugAsync(Exception exception) + => _manager.DebugAsync(Name, exception); public Task DebugAsync(string message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); +#if NETSTANDARD1_3 public Task DebugAsync(FormattableString message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); - public Task DebugAsync(Exception exception) - => _manager.DebugAsync(Name, exception); +#endif } } diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs index 78a8e63cb..8ee52171c 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs @@ -209,7 +209,7 @@ namespace Discord.Net.Queue #endif } - var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); DateTimeOffset? resetTick = null; //Using X-RateLimit-Remaining causes a race condition diff --git a/src/Discord.Net.Core/Net/RateLimitInfo.cs b/src/Discord.Net.Core/Net/RateLimitInfo.cs index 2c2faccf8..3c2b2acd5 100644 --- a/src/Discord.Net.Core/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Core/Net/RateLimitInfo.cs @@ -17,7 +17,7 @@ namespace Discord.Net IsGlobal = headers.TryGetValue("X-RateLimit-Global", out temp) ? bool.Parse(temp) : false; Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) ? int.Parse(temp) : (int?)null; Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) ? int.Parse(temp) : (int?)null; - Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeOffset.FromUnixTimeSeconds(int.Parse(temp)) : (DateTimeOffset?)null; + Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) ? int.Parse(temp) : (int?)null; } } diff --git a/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs b/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs new file mode 100644 index 000000000..8da948d1a --- /dev/null +++ b/src/Discord.Net.Core/Net/Udp/IUdpSocket.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Udp +{ + public interface IUdpSocket + { + event Func ReceivedDatagram; + + void SetCancelToken(CancellationToken cancelToken); + void SetDestination(string host, int port); + + Task StartAsync(); + Task StopAsync(); + + Task SendAsync(byte[] data, int index, int count); + } +} diff --git a/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs b/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs new file mode 100644 index 000000000..07fbd4f57 --- /dev/null +++ b/src/Discord.Net.Core/Net/Udp/UdpSocketProvider.cs @@ -0,0 +1,4 @@ +namespace Discord.Net.Udp +{ + public delegate IUdpSocket UdpSocketProvider(); +} diff --git a/src/Discord.Net.Core/Utils/DateTimeUtils.cs b/src/Discord.Net.Core/Utils/DateTimeUtils.cs index aa127fe29..b36d329d1 100644 --- a/src/Discord.Net.Core/Utils/DateTimeUtils.cs +++ b/src/Discord.Net.Core/Utils/DateTimeUtils.cs @@ -2,14 +2,58 @@ namespace Discord { -internal static class DateTimeUtils -{ - public static DateTimeOffset FromSnowflake(ulong value) - => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); + internal static class DateTimeUtils + { +#if !NETSTANDARD1_3 + //https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs + private const long UnixEpochTicks = 621355968000000000; + private const long UnixEpochSeconds = 62135596800; +#endif - public static DateTimeOffset FromTicks(long ticks) - => new DateTimeOffset(ticks, TimeSpan.Zero); - public static DateTimeOffset? FromTicks(long? ticks) - => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; -} + public static DateTimeOffset FromSnowflake(ulong value) + => FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); + + public static DateTimeOffset FromTicks(long ticks) + => new DateTimeOffset(ticks, TimeSpan.Zero); + public static DateTimeOffset? FromTicks(long? ticks) + => ticks != null ? new DateTimeOffset(ticks.Value, TimeSpan.Zero) : (DateTimeOffset?)null; + + public static DateTimeOffset FromUnixSeconds(long seconds) + { +#if NETSTANDARD1_3 + return DateTimeOffset.FromUnixTimeSeconds(seconds); +#else + long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); +#endif + } + public static DateTimeOffset FromUnixMilliseconds(long seconds) + { +#if NETSTANDARD1_3 + return DateTimeOffset.FromUnixTimeMilliseconds(seconds); +#else + long ticks = seconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; + return new DateTimeOffset(ticks, TimeSpan.Zero); +#endif + } + + public static long ToUnixSeconds(DateTimeOffset dto) + { +#if NETSTANDARD1_3 + return dto.ToUnixTimeSeconds(); +#else + long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond; + return seconds - UnixEpochSeconds; +#endif + } + public static long ToUnixMilliseconds(DateTimeOffset dto) + { +#if NETSTANDARD1_3 + return dto.ToUnixTimeMilliseconds(); +#else + long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; + return seconds - UnixEpochSeconds; +#endif + } + } } diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json index 6a62b9474..16a10f585 100644 --- a/src/Discord.Net.Core/project.json +++ b/src/Discord.Net.Core/project.json @@ -26,25 +26,16 @@ }, "dependencies": { - "Microsoft.Win32.Primitives": "4.3.0", "Newtonsoft.Json": "9.0.1", "System.Collections.Concurrent": "4.3.0", "System.Collections.Immutable": "1.3.0", "System.Interactive.Async": "3.1.0", "System.Net.Http": "4.3.0", - "System.Net.WebSockets.Client": { - "version": "4.3.0", - "type": "build" - } + "System.Runtime.Serialization.Primitives": "4.1.1" }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 4ed019dfb..1a0348d34 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -86,7 +86,7 @@ namespace Discord.Rest await _loggedInEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.CompletedTask; } + protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); } /// public async Task LogoutAsync() @@ -111,7 +111,7 @@ namespace Discord.Rest await _loggedOutEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLogoutAsync() { return Task.CompletedTask; } + protected virtual Task OnLogoutAsync() { return Task.Delay(0); } internal virtual void Dispose(bool disposing) { diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 6bc40571f..87a719538 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,44 +1,28 @@ - - + A core Discord.Net library containing the REST client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 8c1bd45da..bf45db07f 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -21,12 +21,12 @@ namespace Discord.Rest protected override Task OnLoginAsync(TokenType tokenType, string token) { base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); - return Task.CompletedTask; + return Task.Delay(0); } protected override Task OnLogoutAsync() { _applicationInfo = null; - return Task.CompletedTask; + return Task.Delay(0); } /// diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 99edc6f48..b64c288ee 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -139,6 +139,7 @@ namespace Discord.Rest return RestUserMessage.Create(client, channel, client.CurrentUser, model); } +#if NETSTANDARD1_3 public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, string filePath, string text, bool isTTS, RequestOptions options) { @@ -146,6 +147,7 @@ namespace Discord.Rest using (var file = File.OpenRead(filePath)) return await SendFileAsync(channel, client, file, filename, text, isTTS, options).ConfigureAwait(false); } +#endif public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, Stream stream, string filename, string text, bool isTTS, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index 554104d4d..7fbbd21c1 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -8,8 +8,10 @@ namespace Discord.Rest { /// Sends a message to this message channel. new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 573cfef72..5205d8221 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -65,8 +65,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -122,8 +124,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index e2a015c75..2892695c4 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -78,8 +78,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -132,8 +134,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 991b30283..c14899daa 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -57,8 +57,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -104,8 +106,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs index 6127eaf65..dc4178c3c 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs @@ -35,8 +35,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text, bool isTTS, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -82,8 +84,10 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index e7115babd..4f74129dd 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -69,7 +69,7 @@ namespace Discord.Rest public static ImmutableArray ParseTags(string text, IMessageChannel channel, IGuild guild, ImmutableArray userMentions) { - var tags = new SortedList(); + var tags = ImmutableArray.CreateBuilder(); int index = 0; while (true) @@ -94,27 +94,27 @@ namespace Discord.Rest break; } } - tags.Add(index, new Tag(TagType.UserMention, index, content.Length, id, mentionedUser)); + tags.Add(new Tag(TagType.UserMention, index, content.Length, id, mentionedUser)); } else if (MentionUtils.TryParseChannel(content, out id)) { IChannel mentionedChannel = null; if (guild != null) mentionedChannel = guild.GetChannelAsync(id, CacheMode.CacheOnly).GetAwaiter().GetResult(); - tags.Add(index, new Tag(TagType.ChannelMention, index, content.Length, id, mentionedChannel)); + tags.Add(new Tag(TagType.ChannelMention, index, content.Length, id, mentionedChannel)); } else if (MentionUtils.TryParseRole(content, out id)) { IRole mentionedRole = null; if (guild != null) mentionedRole = guild.GetRole(id); - tags.Add(index, new Tag(TagType.RoleMention, index, content.Length, id, mentionedRole)); + tags.Add(new Tag(TagType.RoleMention, index, content.Length, id, mentionedRole)); } else { Emoji emoji; if (Emoji.TryParse(content, out emoji)) - tags.Add(index, new Tag(TagType.Emoji, index, content.Length, id, emoji)); + tags.Add(new Tag(TagType.Emoji, index, content.Length, id, emoji)); } index = endIndex + 1; } @@ -125,7 +125,7 @@ namespace Discord.Rest index = text.IndexOf("@everyone", index); if (index == -1) break; - tags.Add(index, new Tag(TagType.EveryoneMention, index, "@everyone".Length, 0, null)); + tags.Add(new Tag(TagType.EveryoneMention, index, "@everyone".Length, 0, null)); index++; } @@ -135,11 +135,11 @@ namespace Discord.Rest index = text.IndexOf("@here", index); if (index == -1) break; - tags.Add(index, new Tag(TagType.HereMention, index, "@here".Length, 0, null)); + tags.Add(new Tag(TagType.HereMention, index, "@here".Length, 0, null)); index++; } - return tags.Values.ToImmutableArray(); + return tags.ToImmutable(); } public static ImmutableArray FilterTagsByKey(TagType type, ImmutableArray tags) { diff --git a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs similarity index 99% rename from src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs rename to src/Discord.Net.Rest/Net/DefaultRestClient.cs index 588785230..9915ffd53 100644 --- a/src/Discord.Net.Core/Net/Rest/DefaultRestClient.cs +++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace Discord.Net.Rest { - public sealed class DefaultRestClient : IRestClient + internal sealed class DefaultRestClient : IRestClient { private const int HR_SECURECHANNELFAILED = -2146233079; diff --git a/src/Discord.Net.Rest/project.json b/src/Discord.Net.Rest/project.json index b1ad15cb8..7db631b18 100644 --- a/src/Discord.Net.Rest/project.json +++ b/src/Discord.Net.Rest/project.json @@ -29,16 +29,14 @@ "Discord.Net.Core": { "target": "project" }, - "System.IO.FileSystem": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.IO.FileSystem": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 050783f28..720c975c0 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -218,9 +218,6 @@ namespace Discord.API private async Task SendRpcAsyncInternal(string cmd, object payload, Optional evt, RequestOptions options) where TResponse : class { - if (!options.IgnoreState) - CheckState(); - byte[] bytes = null; var guid = Guid.NewGuid(); payload = new API.Rpc.RpcFrame { Cmd = cmd, Event = evt, Args = payload, Nonce = guid }; diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index efd2ea893..4b0ce4c5d 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,48 +1,40 @@ - - + A core Discord.Net library containing the RPC client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - + + + Net\DefaultWebSocketClient.cs + + - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 4.3.0 - + + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.Rpc/DiscordRpcConfig.cs b/src/Discord.Net.Rpc/DiscordRpcConfig.cs index d1e69376c..1866e838b 100644 --- a/src/Discord.Net.Rpc/DiscordRpcConfig.cs +++ b/src/Discord.Net.Rpc/DiscordRpcConfig.cs @@ -1,5 +1,6 @@ using Discord.Net.WebSockets; using Discord.Rest; +using System; namespace Discord.Rpc { @@ -14,6 +15,19 @@ namespace Discord.Rpc public int ConnectionTimeout { get; set; } = 30000; /// Gets or sets the provider used to generate new websocket connections. - public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); + public WebSocketProvider WebSocketProvider { get; set; } + + public DiscordRpcConfig() + { +#if NETSTANDARD1_3 + WebSocketProvider = () => new DefaultWebSocketClient(); +#else + WebSocketProvider = () => + { + throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } } } diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs index af0102574..ba0a31910 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs @@ -46,8 +46,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -100,8 +102,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs index c88621d8f..599df7e50 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs @@ -48,8 +48,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -99,8 +101,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs index a5779bdbb..08780debb 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -51,8 +51,10 @@ namespace Discord.Rpc public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -101,8 +103,10 @@ namespace Discord.Rpc async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json index 8b2db7421..d258fbdcb 100644 --- a/src/Discord.Net.Rpc/project.json +++ b/src/Discord.Net.Rpc/project.json @@ -32,17 +32,15 @@ "Discord.Net.Rest": { "target": "project" }, - "System.IO.Compression": "4.3.0", - "System.Net.WebSockets.Client": "4.3.0" + "System.IO.Compression": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.Net.WebSockets.Client": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs index 378acd22e..adbffc780 100644 --- a/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs @@ -2,6 +2,7 @@ using Discord.API; using Discord.API.Voice; using Discord.Net.Converters; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Newtonsoft.Json; using System; @@ -9,8 +10,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; -using System.Net; -using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -42,19 +41,27 @@ namespace Discord.Audio private readonly IWebSocketClient _webSocketClient; private readonly SemaphoreSlim _connectionLock; private CancellationTokenSource _connectCancelToken; - private UdpClient _udp; - private IPEndPoint _udpEndpoint; - private Task _udpRecieveTask; + private IUdpSocket _udp; private bool _isDisposed; public ulong GuildId { get; } public ConnectionState ConnectionState { get; private set; } - internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, JsonSerializer serializer = null) + internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, UdpSocketProvider udpSocketProvider, JsonSerializer serializer = null) { GuildId = guildId; _connectionLock = new SemaphoreSlim(1, 1); - _udp = new UdpClient(new IPEndPoint(IPAddress.Any, 0)); + _udp = udpSocketProvider(); + _udp.ReceivedDatagram += async (data, index, count) => + { + if (index != 0) + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _receivedPacketEvent.InvokeAsync(data).ConfigureAwait(false); + }; _webSocketClient = webSocketProvider(); //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+) @@ -93,6 +100,7 @@ namespace Discord.Audio if (disposing) { _connectCancelToken?.Dispose(); + (_udp as IDisposable)?.Dispose(); (_webSocketClient as IDisposable)?.Dispose(); } _isDisposed = true; @@ -111,18 +119,14 @@ namespace Discord.Audio } public async Task SendAsync(byte[] data, int bytes) { - if (_udpEndpoint != null) - { - await _udp.SendAsync(data, bytes, _udpEndpoint).ConfigureAwait(false); - await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false); - } - + await _udp.SendAsync(data, 0, bytes).ConfigureAwait(false); + await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false); } //WebSocket public async Task SendHeartbeatAsync(RequestOptions options = null) { - await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false); + await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false); } public async Task SendIdentityAsync(ulong userId, string sessionId, string token) { @@ -171,9 +175,13 @@ namespace Discord.Audio try { _connectCancelToken = new CancellationTokenSource(); - _webSocketClient.SetCancelToken(_connectCancelToken.Token); + var cancelToken = _connectCancelToken.Token; + + _webSocketClient.SetCancelToken(cancelToken); await _webSocketClient.ConnectAsync(url).ConfigureAwait(false); - _udpRecieveTask = ReceiveAsync(_connectCancelToken.Token); + + _udp.SetCancelToken(cancelToken); + await _udp.StartAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Connected; } @@ -202,8 +210,7 @@ namespace Discord.Audio catch { } //Wait for tasks to complete - await _udpRecieveTask.ConfigureAwait(false); - + await _udp.StopAsync().ConfigureAwait(false); await _webSocketClient.DisconnectAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Disconnected; @@ -221,22 +228,9 @@ namespace Discord.Audio await _sentDiscoveryEvent.InvokeAsync().ConfigureAwait(false); } - public void SetUdpEndpoint(IPEndPoint endpoint) + public void SetUdpEndpoint(string host, int port) { - _udpEndpoint = endpoint; - } - private async Task ReceiveAsync(CancellationToken cancelToken) - { - var closeTask = Task.Delay(-1, cancelToken); - while (!cancelToken.IsCancellationRequested) - { - var receiveTask = _udp.ReceiveAsync(); - var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); - if (task == closeTask) - break; - - await _receivedPacketEvent.InvokeAsync(receiveTask.Result.Buffer).ConfigureAwait(false); - } + _udp.SetDestination(host, port); } //Helpers diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 04eec4541..a3774a76c 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -71,7 +71,7 @@ namespace Discord.Audio e.ErrorContext.Handled = true; }; - ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider); + ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); @@ -204,10 +204,8 @@ namespace Discord.Audio _heartbeatTime = 0; _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token); - - var entry = await Dns.GetHostEntryAsync(_url).ConfigureAwait(false); - - ApiClient.SetUdpEndpoint(new IPEndPoint(entry.AddressList[0], data.Port)); + + ApiClient.SetUdpEndpoint(_url, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); } break; diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index dd6541412..c7ca40c13 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,9 +1,8 @@ - - + A core Discord.Net library containing the WebSocket client and models. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 true Discord.Net.WebSocket discord;discordapp @@ -11,48 +10,27 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - - - 1.0.0-alpha-20161104-2 - All - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - + + + + + + + - - - False - - $(DefineConstants);RELEASE $(NoWarn);CS1573;CS1591 true true - \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index c28e30b51..a61d21214 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -4,6 +4,7 @@ using Discord.Audio; using Discord.Logging; using Discord.Net.Converters; using Discord.Net.Queue; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; using Newtonsoft.Json; @@ -56,6 +57,7 @@ namespace Discord.WebSocket internal AudioMode AudioMode { get; private set; } internal ClientState State { get; private set; } internal int ConnectionTimeout { get; private set; } + internal UdpSocketProvider UdpSocketProvider { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; } internal bool DownloadUsersOnGuildAvailable { get; private set; } @@ -76,6 +78,7 @@ namespace Discord.WebSocket MessageCacheSize = config.MessageCacheSize; LargeThreshold = config.LargeThreshold; AudioMode = config.AudioMode; + UdpSocketProvider = config.UdpSocketProvider; WebSocketProvider = config.WebSocketProvider; DownloadUsersOnGuildAvailable = config.DownloadUsersOnGuildAvailable; ConnectionTimeout = config.ConnectionTimeout; @@ -115,7 +118,7 @@ namespace Discord.WebSocket GuildAvailable += g => { var _ = g.DownloadUsersAsync(); - return Task.CompletedTask; + return Task.Delay(0); }; } @@ -512,7 +515,7 @@ namespace Discord.WebSocket await ApiClient.SendStatusUpdateAsync( status, status == UserStatus.AFK, - statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, + statusSince != null ? DateTimeUtils.ToUnixMilliseconds(_statusSince.Value) : (long?)null, gameModel).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 9bf337f8e..04001a5aa 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -1,6 +1,8 @@ using Discord.Audio; +using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; +using System; namespace Discord.WebSocket { @@ -27,9 +29,30 @@ namespace Discord.WebSocket public AudioMode AudioMode { get; set; } = AudioMode.Disabled; /// Gets or sets the provider used to generate new websocket connections. - public WebSocketProvider WebSocketProvider { get; set; } = () => new DefaultWebSocketClient(); + public WebSocketProvider WebSocketProvider { get; set; } + /// Gets or sets the provider used to generate new udp sockets. + public UdpSocketProvider UdpSocketProvider { get; set; } /// Gets or sets whether or not all users should be downloaded as guilds come available. public bool DownloadUsersOnGuildAvailable { get; set; } = false; + + public DiscordSocketConfig() + { +#if NETSTANDARD1_3 + WebSocketProvider = () => new DefaultWebSocketClient(); + UdpSocketProvider = () => new DefaultUdpSocket(); +#else + WebSocketProvider = () => + { + throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; + UdpSocketProvider = () => + { + throw new InvalidOperationException("The default UDP provider is not supported on this platform.\n" + + "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index f1d4221a2..0f8e1e156 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -12,8 +12,10 @@ namespace Discord.WebSocket /// Sends a message to this message channel. new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); +#if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); +#endif /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(Stream stream, string filename, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 48f87764b..34f67c1f0 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -68,8 +68,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -130,8 +132,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 93407e22e..980b6dc2a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -91,8 +91,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -193,8 +195,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 9b687c9fb..903263e89 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -74,8 +74,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); +#if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, options); +#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, options); @@ -131,8 +133,10 @@ namespace Discord.WebSocket => SocketChannelHelper.GetMessagesAsync(this, Discord, _messages, fromMessage.Id, dir, limit, mode, options); async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); +#if NETSTANDARD1_3 async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, options).ConfigureAwait(false); +#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs new file mode 100644 index 000000000..050833fb8 --- /dev/null +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -0,0 +1,129 @@ +#if NETSTANDARD1_3 +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Udp +{ + internal class DefaultUdpSocket : IUdpSocket + { + public event Func ReceivedDatagram; + + private readonly SemaphoreSlim _lock; + private UdpClient _udp; + private IPEndPoint _destination; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private Task _task; + private bool _isDisposed; + + public DefaultUdpSocket() + { + _lock = new SemaphoreSlim(1, 1); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + StopInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + + public async Task StartAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StartInternalAsync(_cancelToken).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StartInternalAsync(CancellationToken cancelToken) + { + await StopInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _udp = new UdpClient(); + + _task = RunAsync(_cancelToken); + } + public async Task StopAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StopInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StopInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_udp != null) + { + try { _udp.Dispose(); } + catch { } + _udp = null; + } + } + + public void SetDestination(string host, int port) + { + var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult(); + _destination = new IPEndPoint(entry.AddressList[0], port); + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count) + { + if (index != 0) //Should never happen? + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _udp.SendAsync(data, count, _destination).ConfigureAwait(false); + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var closeTask = Task.Delay(-1, cancelToken); + while (!cancelToken.IsCancellationRequested) + { + var receiveTask = _udp.ReceiveAsync(); + var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); + if (task == closeTask) + break; + + var result = receiveTask.Result; + await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs similarity index 87% rename from src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs rename to src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index ffa96dba7..acfff94f2 100644 --- a/src/Discord.Net.Core/Net/WebSockets/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -1,4 +1,5 @@ -using System; +#if NETSTANDARD1_3 +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -9,7 +10,7 @@ using System.Threading.Tasks; namespace Discord.Net.WebSockets { - public class DefaultWebSocketClient : IWebSocketClient + internal class DefaultWebSocketClient : IWebSocketClient { public const int ReceiveChunkSize = 16 * 1024; //16KB public const int SendChunkSize = 4 * 1024; //4KB @@ -19,7 +20,7 @@ namespace Discord.Net.WebSockets public event Func TextMessage; public event Func Closed; - private readonly SemaphoreSlim _sendLock; + private readonly SemaphoreSlim _lock; private readonly Dictionary _headers; private ClientWebSocket _client; private Task _task; @@ -29,7 +30,7 @@ namespace Discord.Net.WebSockets public DefaultWebSocketClient() { - _sendLock = new SemaphoreSlim(1, 1); + _lock = new SemaphoreSlim(1, 1); _cancelTokenSource = new CancellationTokenSource(); _cancelToken = CancellationToken.None; _parentToken = CancellationToken.None; @@ -40,7 +41,7 @@ namespace Discord.Net.WebSockets if (!_isDisposed) { if (disposing) - _client.Dispose(); + DisconnectInternalAsync(true).GetAwaiter().GetResult(); _isDisposed = true; } } @@ -51,14 +52,14 @@ namespace Discord.Net.WebSockets public async Task ConnectAsync(string host) { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { await ConnectInternalAsync(host).ConfigureAwait(false); } finally { - _sendLock.Release(); + _lock.Release(); } } private async Task ConnectInternalAsync(string host) @@ -83,27 +84,33 @@ namespace Discord.Net.WebSockets public async Task DisconnectAsync() { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { await DisconnectInternalAsync().ConfigureAwait(false); } finally { - _sendLock.Release(); + _lock.Release(); } } - private async Task DisconnectInternalAsync() + private async Task DisconnectInternalAsync(bool isDisposing = false) { try { _cancelTokenSource.Cancel(false); } catch { } - await (_task ?? Task.CompletedTask).ConfigureAwait(false); + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); if (_client != null && _client.State == WebSocketState.Open) { var token = new CancellationToken(); - await _client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", token); - _client.Dispose(); + if (!isDisposing) + { + try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", token); } + catch { } + } + try { _client.Dispose(); } + catch { } _client = null; } } @@ -120,7 +127,7 @@ namespace Discord.Net.WebSockets public async Task SendAsync(byte[] data, int index, int count, bool isText) { - await _sendLock.WaitAsync().ConfigureAwait(false); + await _lock.WaitAsync().ConfigureAwait(false); try { if (_client == null) return; @@ -143,7 +150,7 @@ namespace Discord.Net.WebSockets } finally { - _sendLock.Release(); + _lock.Release(); } } @@ -181,11 +188,15 @@ namespace Discord.Net.WebSockets //Use the internal buffer if we can get it resultCount = (int)stream.Length; +#if NETSTANDARD1_3 ArraySegment streamBuffer; if (stream.TryGetBuffer(out streamBuffer)) result = streamBuffer.Array; else result = stream.ToArray(); +#else + result = stream.ToArray(); +#endif } } else @@ -217,3 +228,4 @@ namespace Discord.Net.WebSockets } } } +#endif \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/project.json b/src/Discord.Net.WebSocket/project.json index 46eb0eccd..4ee07e2ac 100644 --- a/src/Discord.Net.WebSocket/project.json +++ b/src/Discord.Net.WebSocket/project.json @@ -37,19 +37,17 @@ "target": "project" }, "System.IO.Compression": "4.3.0", - "System.Net.NameResolution": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Net.WebSockets.Client": "4.3.0", "System.Runtime.InteropServices": "4.3.0" }, "frameworks": { + "netstandard1.1": {}, "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] + "dependencies": { + "System.Net.NameResolution": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.Net.WebSockets.Client": "4.3.0" + } } } } \ No newline at end of file diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index ae9eb3e2a..be5f09125 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,23 +1,19 @@ - - + An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0-beta2 - netstandard1.3 + netstandard1.1;netstandard1.3 Discord.Net discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net - $(PackageTargetFallback);dotnet5.4;dnxcore50;portable-net45+win8 - - @@ -25,18 +21,4 @@ - - - 1.0.0-alpha-20161104-2 - All - - - - - False - - - $(DefineConstants);RELEASE - - \ No newline at end of file diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json index 98b3b6211..c02f510f9 100644 --- a/src/Discord.Net/project.json +++ b/src/Discord.Net/project.json @@ -32,12 +32,7 @@ }, "frameworks": { - "netstandard1.3": { - "imports": [ - "dotnet5.4", - "dnxcore50", - "portable-net45+win8" - ] - } + "netstandard1.1": {}, + "netstandard1.3": {} } } \ No newline at end of file From b01200f929f18ad9c4f11e2905cd7857c07409d6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 06:33:21 -0400 Subject: [PATCH 081/263] Cleaned up primitive type readers. Fixed TimeSpan reader. --- src/Discord.Net.Commands/CommandService.cs | 22 ++++--------------- src/Discord.Net.Commands/PrimitiveParsers.cs | 12 +++++----- ...leTypeReader.cs => PrimitiveTypeReader.cs} | 16 +++++++++++--- 3 files changed, 24 insertions(+), 26 deletions(-) rename src/Discord.Net.Commands/Readers/{SimpleTypeReader.cs => PrimitiveTypeReader.cs} (57%) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 9c2440b7c..010a0ee8a 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -37,24 +37,7 @@ namespace Discord.Commands _typeReaders = new ConcurrentDictionary>(); _defaultTypeReaders = new ConcurrentDictionary - { - [typeof(bool)] = new SimpleTypeReader(), - [typeof(char)] = new SimpleTypeReader(), - [typeof(string)] = new SimpleTypeReader(), - [typeof(byte)] = new SimpleTypeReader(), - [typeof(sbyte)] = new SimpleTypeReader(), - [typeof(ushort)] = new SimpleTypeReader(), - [typeof(short)] = new SimpleTypeReader(), - [typeof(uint)] = new SimpleTypeReader(), - [typeof(int)] = new SimpleTypeReader(), - [typeof(ulong)] = new SimpleTypeReader(), - [typeof(long)] = new SimpleTypeReader(), - [typeof(float)] = new SimpleTypeReader(), - [typeof(double)] = new SimpleTypeReader(), - [typeof(decimal)] = new SimpleTypeReader(), - [typeof(DateTime)] = new SimpleTypeReader(), - [typeof(DateTimeOffset)] = new SimpleTypeReader(), - + { [typeof(IMessage)] = new MessageTypeReader(), [typeof(IUserMessage)] = new MessageTypeReader(), [typeof(IChannel)] = new ChannelTypeReader(), @@ -72,6 +55,9 @@ namespace Discord.Commands [typeof(IGroupUser)] = new UserTypeReader(), [typeof(IGuildUser)] = new UserTypeReader(), }; + foreach (var type in PrimitiveParsers.SupportedTypes) + _defaultTypeReaders[type] = PrimitiveTypeReader.Create(type); + _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; } diff --git a/src/Discord.Net.Commands/PrimitiveParsers.cs b/src/Discord.Net.Commands/PrimitiveParsers.cs index 0b00a90e0..623ddafa7 100644 --- a/src/Discord.Net.Commands/PrimitiveParsers.cs +++ b/src/Discord.Net.Commands/PrimitiveParsers.cs @@ -8,9 +8,11 @@ namespace Discord.Commands internal static class PrimitiveParsers { - private static readonly IReadOnlyDictionary _parsers; + private static readonly Lazy> _parsers = new Lazy>(CreateParsers); - static PrimitiveParsers() + public static IEnumerable SupportedTypes = _parsers.Value.Keys; + + static IReadOnlyDictionary CreateParsers() { var parserBuilder = ImmutableDictionary.CreateBuilder(); parserBuilder[typeof(bool)] = (TryParseDelegate)bool.TryParse; @@ -34,10 +36,10 @@ namespace Discord.Commands value = str; return true; }; - _parsers = parserBuilder.ToImmutable(); + return parserBuilder.ToImmutable(); } - public static TryParseDelegate Get() => (TryParseDelegate)_parsers[typeof(T)]; - public static Delegate Get(Type type) => _parsers[type]; + public static TryParseDelegate Get() => (TryParseDelegate)_parsers.Value[typeof(T)]; + public static Delegate Get(Type type) => _parsers.Value[type]; } } diff --git a/src/Discord.Net.Commands/Readers/SimpleTypeReader.cs b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs similarity index 57% rename from src/Discord.Net.Commands/Readers/SimpleTypeReader.cs rename to src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs index ad939e59d..824154cd0 100644 --- a/src/Discord.Net.Commands/Readers/SimpleTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs @@ -1,12 +1,22 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Discord.Commands { - internal class SimpleTypeReader : TypeReader + internal static class PrimitiveTypeReader + { + public static TypeReader Create(Type type) + { + type = typeof(PrimitiveTypeReader<>).MakeGenericType(type); + return Activator.CreateInstance(type) as TypeReader; + } + } + + internal class PrimitiveTypeReader : TypeReader { private readonly TryParseDelegate _tryParse; - public SimpleTypeReader() + public PrimitiveTypeReader() { _tryParse = PrimitiveParsers.Get(); } From 747dd9869822284e709a45863c35efe8f8f245eb Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 06:43:45 -0400 Subject: [PATCH 082/263] Add DefaultWebSocketClient to Rpc's project.json --- src/Discord.Net.Rpc/project.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json index d258fbdcb..ba7cebf9f 100644 --- a/src/Discord.Net.Rpc/project.json +++ b/src/Discord.Net.Rpc/project.json @@ -13,6 +13,10 @@ } }, + "publishOptions": { + "includeFiles": [ "../Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs" ] + }, + "configurations": { "Release": { "buildOptions": { From d3d3bb155d827d48ee65b27049cbaf9a4a56f24e Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 06:48:43 -0400 Subject: [PATCH 083/263] Wrong key... --- src/Discord.Net.Rpc/project.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json index ba7cebf9f..a638f4588 100644 --- a/src/Discord.Net.Rpc/project.json +++ b/src/Discord.Net.Rpc/project.json @@ -3,6 +3,12 @@ "description": "A core Discord.Net library containing the RPC client and models.", "authors": [ "RogueException" ], + "buildOptions": { + "compile": { + "includeFiles": [ "../Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs" ] + } + }, + "packOptions": { "tags": [ "discord", "discordapp" ], "licenseUrl": "http://opensource.org/licenses/MIT", @@ -13,10 +19,6 @@ } }, - "publishOptions": { - "includeFiles": [ "../Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs" ] - }, - "configurations": { "Release": { "buildOptions": { From b166683704646463294aff7b25d30c917deedc03 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 07:32:26 -0400 Subject: [PATCH 084/263] Added missing IDisposables --- src/Discord.Net.Rest/Net/DefaultRestClient.cs | 2 +- src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs | 2 +- src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Rest/Net/DefaultRestClient.cs b/src/Discord.Net.Rest/Net/DefaultRestClient.cs index 9915ffd53..39b94294f 100644 --- a/src/Discord.Net.Rest/Net/DefaultRestClient.cs +++ b/src/Discord.Net.Rest/Net/DefaultRestClient.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; namespace Discord.Net.Rest { - internal sealed class DefaultRestClient : IRestClient + internal sealed class DefaultRestClient : IRestClient, IDisposable { private const int HR_SECURECHANNELFAILED = -2146233079; diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs index 050833fb8..20620a3be 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Discord.Net.Udp { - internal class DefaultUdpSocket : IUdpSocket + internal class DefaultUdpSocket : IUdpSocket, IDisposable { public event Func ReceivedDatagram; diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index acfff94f2..b2ebffabe 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Discord.Net.WebSockets { - internal class DefaultWebSocketClient : IWebSocketClient + internal class DefaultWebSocketClient : IWebSocketClient, IDisposable { public const int ReceiveChunkSize = 16 * 1024; //16KB public const int SendChunkSize = 4 * 1024; //4KB From 50688384caba7aba9e21db9bf37e33c4eb914b04 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 07:33:54 -0400 Subject: [PATCH 085/263] Added WS4Net Provider --- Discord.Net.sln | 40 +++-- .../Discord.Net.Providers.WS4Net.csproj | 29 +++ .../WS4NetClient.cs | 166 ++++++++++++++++++ .../WS4NetProvider.cs | 9 + src/Discord.Net.Providers.WS4Net/project.json | 38 ++++ 5 files changed, 270 insertions(+), 12 deletions(-) create mode 100644 src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj create mode 100644 src/Discord.Net.Providers.WS4Net/WS4NetClient.cs create mode 100644 src/Discord.Net.Providers.WS4Net/WS4NetProvider.cs create mode 100644 src/Discord.Net.Providers.WS4Net/project.json diff --git a/Discord.Net.sln b/Discord.Net.sln index 206c5b54a..9663b21dc 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,11 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.25914.0 +VisualStudioVersion = 15.0.26014.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}" ProjectSection(SolutionItems) = preProject - global.json = global.json README.md = README.md EndProjectSection EndProject @@ -23,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Commands", "src EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.WebSocket", "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj", "{688FD1D8-7F01-4539-B2E9-F473C5D699C7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.WS4Net", "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj", "{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,8 +42,8 @@ Global {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x64.Build.0 = Debug|Any CPU {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.ActiveCfg = Debug|Any CPU {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.Build.0 = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Debug|Any CPU + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.ActiveCfg = Debug|Any CPU {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.Build.0 = Debug|Any CPU {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x86.ActiveCfg = Debug|Any CPU @@ -51,8 +54,8 @@ Global {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x64.Build.0 = Debug|x64 {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x86.ActiveCfg = Debug|x86 {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x86.Build.0 = Debug|x86 - {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Debug|Any CPU + {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|Any CPU.Build.0 = Release|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x64.ActiveCfg = Release|x64 {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x64.Build.0 = Release|x64 {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Release|x86.ActiveCfg = Release|x86 @@ -63,8 +66,8 @@ Global {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x64.Build.0 = Debug|Any CPU {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x86.ActiveCfg = Debug|Any CPU {BFC6DC28-0351-4573-926A-D4124244C04F}.Debug|x86.Build.0 = Debug|Any CPU - {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Debug|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|Any CPU.Build.0 = Release|Any CPU {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.ActiveCfg = Debug|Any CPU {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x64.Build.0 = Debug|Any CPU {BFC6DC28-0351-4573-926A-D4124244C04F}.Release|x86.ActiveCfg = Debug|Any CPU @@ -75,8 +78,8 @@ Global {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x64.Build.0 = Debug|Any CPU {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.ActiveCfg = Debug|Any CPU {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Debug|x86.Build.0 = Debug|Any CPU - {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Debug|Any CPU + {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|Any CPU.Build.0 = Release|Any CPU {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.ActiveCfg = Debug|Any CPU {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x64.Build.0 = Debug|Any CPU {5688A353-121E-40A1-8BFA-B17B91FB48FB}.Release|x86.ActiveCfg = Debug|Any CPU @@ -87,8 +90,8 @@ Global {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x64.Build.0 = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x86.ActiveCfg = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|x86.Build.0 = Debug|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Debug|Any CPU + {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|Any CPU.Build.0 = Release|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x64.ActiveCfg = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x64.Build.0 = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Release|x86.ActiveCfg = Debug|Any CPU @@ -105,6 +108,18 @@ Global {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x64.Build.0 = Release|x64 {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x86.ActiveCfg = Release|x86 {688FD1D8-7F01-4539-B2E9-F473C5D699C7}.Release|x86.Build.0 = Release|x86 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|x64.ActiveCfg = Debug|x64 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|x64.Build.0 = Debug|x64 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|x86.ActiveCfg = Debug|x86 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Debug|x86.Build.0 = Debug|x86 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|Any CPU.Build.0 = Release|Any CPU + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x64.ActiveCfg = Release|x64 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x64.Build.0 = Release|x64 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x86.ActiveCfg = Release|x86 + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -113,5 +128,6 @@ Global {BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} + {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} EndGlobalSection EndGlobal diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj new file mode 100644 index 000000000..963de8269 --- /dev/null +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -0,0 +1,29 @@ + + + An optional WebSocket client provider for Discord.Net using WebSocket4Net + 1.0.0-beta2 + net45 + true + Discord.Net.Providers.WS4Net + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + + + + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs new file mode 100644 index 000000000..7ab8685ba --- /dev/null +++ b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs @@ -0,0 +1,166 @@ +using Discord.Net.WebSockets; +using System; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Linq; +using WebSocket4Net; +using WS4NetSocket = WebSocket4Net.WebSocket; + +namespace Discord.Net.Providers.WS4Net +{ + internal class WS4NetClient : IWebSocketClient, IDisposable + { + public event Func BinaryMessage; + public event Func TextMessage; + public event Func Closed; + + private readonly SemaphoreSlim _lock; + private readonly Dictionary _headers; + private WS4NetSocket _client; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private ManualResetEventSlim _waitUntilConnect; + private bool _isDisposed; + + public WS4NetClient() + { + _headers = new Dictionary(); + _lock = new SemaphoreSlim(1, 1); + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationToken.None; + _parentToken = CancellationToken.None; + _waitUntilConnect = new ManualResetEventSlim(); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + DisconnectInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + public async Task ConnectAsync(string host) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await ConnectInternalAsync(host).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + private async Task ConnectInternalAsync(string host) + { + await DisconnectInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _client = new WS4NetSocket(host, customHeaderItems: _headers.ToList()) + { + EnableAutoSendPing = false, + NoDelay = true, + Proxy = null + }; + + _client.MessageReceived += OnTextMessage; + _client.DataReceived += OnBinaryMessage; + _client.Opened += OnConnected; + _client.Closed += OnClosed; + + _client.Open(); + _waitUntilConnect.Wait(_cancelToken); + } + + public async Task DisconnectAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + private Task DisconnectInternalAsync(bool isDisposing = false) + { + _cancelTokenSource.Cancel(); + if (_client == null) + return Task.Delay(0); + + if (_client.State == WebSocketState.Open) + { + try { _client.Close(1000, ""); } + catch { } + } + + _client.MessageReceived -= OnTextMessage; + _client.DataReceived -= OnBinaryMessage; + _client.Opened -= OnConnected; + _client.Closed -= OnClosed; + + try { _client.Dispose(); } + catch { } + _client = null; + + _waitUntilConnect.Reset(); + return Task.Delay(0); + } + + public void SetHeader(string key, string value) + { + _headers[key] = value; + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count, bool isText) + { + await _lock.WaitAsync(_cancelToken).ConfigureAwait(false); + try + { + if (isText) + _client.Send(Encoding.UTF8.GetString(data, index, count)); + else + _client.Send(data, index, count); + } + finally + { + _lock.Release(); + } + } + + private void OnTextMessage(object sender, MessageReceivedEventArgs e) + { + TextMessage(e.Message).GetAwaiter().GetResult(); + } + private void OnBinaryMessage(object sender, DataReceivedEventArgs e) + { + BinaryMessage(e.Data, 0, e.Data.Count()).GetAwaiter().GetResult(); + } + private void OnConnected(object sender, object e) + { + _waitUntilConnect.Set(); + } + private void OnClosed(object sender, object e) + { + var ex = (e as SuperSocket.ClientEngine.ErrorEventArgs)?.Exception ?? new Exception("Unexpected close"); + Closed(ex).GetAwaiter().GetResult(); + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Providers.WS4Net/WS4NetProvider.cs b/src/Discord.Net.Providers.WS4Net/WS4NetProvider.cs new file mode 100644 index 000000000..166e767d0 --- /dev/null +++ b/src/Discord.Net.Providers.WS4Net/WS4NetProvider.cs @@ -0,0 +1,9 @@ +using Discord.Net.WebSockets; + +namespace Discord.Net.Providers.WS4Net +{ + public static class WS4NetProvider + { + public static readonly WebSocketProvider Instance = () => new WS4NetClient(); + } +} diff --git a/src/Discord.Net.Providers.WS4Net/project.json b/src/Discord.Net.Providers.WS4Net/project.json new file mode 100644 index 000000000..4e35d6bbb --- /dev/null +++ b/src/Discord.Net.Providers.WS4Net/project.json @@ -0,0 +1,38 @@ +{ + "version": "1.0.0-*", + "description": "An optional WebSocket client provider for Discord.Net using WebSocket4Net.", + "authors": [ "RogueException", "foxbot" ], + + "packOptions": { + "tags": [ "discord", "discordapp" ], + "licenseUrl": "http://opensource.org/licenses/MIT", + "projectUrl": "https://github.com/RogueException/Discord.Net", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + } + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Discord.Net.Core": { + "target": "project" + }, + "WebSocket4Net": "0.14.1" + }, + + "frameworks": { + "net45": {} + } +} \ No newline at end of file From 2778d0bdbe3ad91cc32813ea7290011734dbe6c8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 08:22:05 -0400 Subject: [PATCH 086/263] Fixes #414 --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 8 ++++---- .../Entities/Users/SocketVoiceState.cs | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index a61d21214..fde2d100b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1561,7 +1561,7 @@ namespace Discord.WebSocket if (data.ChannelId != null) { - before = guild.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false); + before = guild.GetVoiceState(data.UserId)?.Clone() ?? SocketVoiceState.Default; after = guild.AddOrUpdateVoiceState(State, data); if (data.UserId == CurrentUser.Id) { @@ -1570,7 +1570,7 @@ namespace Discord.WebSocket } else { - before = guild.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false); + before = guild.RemoveVoiceState(data.UserId) ?? SocketVoiceState.Default; after = SocketVoiceState.Create(null, data); } @@ -1583,12 +1583,12 @@ namespace Discord.WebSocket { if (data.ChannelId != null) { - before = groupChannel.GetVoiceState(data.UserId)?.Clone() ?? new SocketVoiceState(null, null, false, false, false); + before = groupChannel.GetVoiceState(data.UserId)?.Clone() ?? SocketVoiceState.Default; after = groupChannel.AddOrUpdateVoiceState(State, data); } else { - before = groupChannel.RemoveVoiceState(data.UserId) ?? new SocketVoiceState(null, null, false, false, false); + before = groupChannel.RemoveVoiceState(data.UserId) ?? SocketVoiceState.Default; after = SocketVoiceState.Create(null, data); } user = groupChannel.GetUser(data.UserId); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs index ed4036362..2a0eeed8f 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs @@ -8,6 +8,8 @@ namespace Discord.WebSocket [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public struct SocketVoiceState : IVoiceState { + public static readonly SocketVoiceState Default = new SocketVoiceState(null, null, false, false, false, false, false); + [Flags] private enum Flags : byte { @@ -30,7 +32,7 @@ namespace Discord.WebSocket public bool IsSelfMuted => (_voiceStates & Flags.SelfMuted) != 0; public bool IsSelfDeafened => (_voiceStates & Flags.SelfDeafened) != 0; - internal SocketVoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isSuppressed) + internal SocketVoiceState(SocketVoiceChannel voiceChannel, string sessionId, bool isSelfMuted, bool isSelfDeafened, bool isMuted, bool isDeafened, bool isSuppressed) { VoiceChannel = voiceChannel; VoiceSessionId = sessionId; @@ -40,13 +42,17 @@ namespace Discord.WebSocket voiceStates |= Flags.SelfMuted; if (isSelfDeafened) voiceStates |= Flags.SelfDeafened; + if (isMuted) + voiceStates |= Flags.Muted; + if (isDeafened) + voiceStates |= Flags.Deafened; if (isSuppressed) voiceStates |= Flags.Suppressed; _voiceStates = voiceStates; } internal static SocketVoiceState Create(SocketVoiceChannel voiceChannel, Model model) { - return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Suppress); + return new SocketVoiceState(voiceChannel, model.SessionId, model.SelfMute, model.SelfDeaf, model.Mute, model.Deaf, model.Suppress); } public override string ToString() => VoiceChannel?.Name ?? "Unknown"; From 3927758b702498c88e573cd39abcf045f4deead8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 08:22:12 -0400 Subject: [PATCH 087/263] Removed Guild.VoiceStates --- src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index d5d93f991..b63b47403 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -84,7 +84,6 @@ namespace Discord.WebSocket public IReadOnlyCollection Features => _features; public IReadOnlyCollection Users => _members.ToReadOnlyCollection(); public IReadOnlyCollection Roles => _roles.ToReadOnlyCollection(); - public IReadOnlyCollection VoiceStates => _voiceStates.ToReadOnlyCollection(); internal SocketGuild(DiscordSocketClient client, ulong id) : base(client, id) From 1efcd3daf699b99330862364006ec1269f534dc7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 09:13:42 -0400 Subject: [PATCH 088/263] Added request retry modes --- .../API/DiscordRestApiClient.cs | 21 ++++++---- src/Discord.Net.Core/DiscordConfig.cs | 3 ++ .../Entities/Users/IGuildUser.cs | 1 + .../Net/Queue/RequestQueue.cs | 8 +++- .../Net/Queue/RequestQueueBucket.cs | 39 ++++++++++++++----- .../Net/Queue/Requests/JsonRestRequest.cs | 2 +- .../Queue/Requests/MultipartRestRequest.cs | 2 +- .../Net/Queue/Requests/RestRequest.cs | 5 +-- src/Discord.Net.Core/RequestOptions.cs | 13 +++++-- src/Discord.Net.Core/RetryMode.cs | 22 +++++++++++ .../API/DiscordRpcApiClient.cs | 6 +-- .../API/DiscordSocketApiClient.cs | 7 ++-- .../DiscordSocketClient.Events.cs | 2 - .../DiscordSocketClient.cs | 2 +- 14 files changed, 96 insertions(+), 37 deletions(-) create mode 100644 src/Discord.Net.Core/RetryMode.cs diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index 0d0b7914b..61e56ca13 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -32,26 +32,29 @@ namespace Discord.API protected readonly JsonSerializer _serializer; protected readonly SemaphoreSlim _stateLock; private readonly RestClientProvider _restClientProvider; - private readonly string _userAgent; protected string _authToken; protected bool _isDisposed; private CancellationTokenSource _loginCancelToken; private IRestClient _restClient; + private bool _fetchCurrentUser; + + public RetryMode DefaultRetryMode { get; } + public string UserAgent { get; } public LoginState LoginState { get; private set; } public TokenType AuthTokenType { get; private set; } public User CurrentUser { get; private set; } public RequestQueue RequestQueue { get; private set; } - internal bool FetchCurrentUser { get; set; } - public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, JsonSerializer serializer = null, RequestQueue requestQueue = null) + public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, + JsonSerializer serializer = null, RequestQueue requestQueue = null, bool fetchCurrentUser = true) { _restClientProvider = restClientProvider; - _userAgent = userAgent; + UserAgent = userAgent; _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; RequestQueue = requestQueue; - FetchCurrentUser = true; + _fetchCurrentUser = fetchCurrentUser; _stateLock = new SemaphoreSlim(1, 1); @@ -61,7 +64,7 @@ namespace Discord.API { _restClient = _restClientProvider(baseUrl); _restClient.SetHeader("accept", "*/*"); - _restClient.SetHeader("user-agent", _userAgent); + _restClient.SetHeader("user-agent", UserAgent); _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); } internal static string GetPrefixedToken(TokenType tokenType, string token) @@ -120,8 +123,8 @@ namespace Discord.API _authToken = token; _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); - if (FetchCurrentUser) - CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true }).ConfigureAwait(false); + if (_fetchCurrentUser) + CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); LoginState = LoginState.LoggedIn; } @@ -257,6 +260,8 @@ namespace Discord.API { if (!request.Options.IgnoreState) CheckState(); + if (request.Options.RetryMode == null) + request.Options.RetryMode = DefaultRetryMode; var stopwatch = Stopwatch.StartNew(); var responseStream = await RequestQueue.SendAsync(request).ConfigureAwait(false); diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index b35f0d745..bb7077472 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -19,6 +19,9 @@ namespace Discord public const int MaxMessagesPerBatch = 100; public const int MaxUsersPerBatch = 1000; + /// Gets or sets how a request should act in the case of an error, by default. + public RetryMode DefaultRetryMode { get; set; } = RetryMode.AlwaysRetry; + /// Gets or sets the minimum log level severity that will be sent to the LogMessage event. public LogSeverity LogLevel { get; set; } = LogSeverity.Info; } diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index b48c76a37..ab447e520 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -12,6 +12,7 @@ namespace Discord DateTimeOffset? JoinedAt { get; } /// Gets the nickname for this user. string Nickname { get; } + /// Gets the guild-level permissions for this user. GuildPermissions GuildPermissions { get; } /// Gets the guild for this user. diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs index 1ea586481..52ad90f11 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueue.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Concurrent; +#if DEBUG_LIMITS using System.Diagnostics; +#endif using System.IO; using System.Linq; using System.Threading; @@ -63,7 +65,11 @@ namespace Discord.Net.Queue public async Task SendAsync(RestRequest request) { - request.CancelToken = _requestCancelToken; + if (request.Options.CancelToken.CanBeCanceled) + request.Options.CancelToken = CancellationTokenSource.CreateLinkedTokenSource(_requestCancelToken, request.Options.CancelToken).Token; + else + request.Options.CancelToken = _requestCancelToken; + var bucket = GetOrCreateBucket(request.Options.BucketId, request); return await bucket.SendAsync(request).ConfigureAwait(false); } diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs index 8ee52171c..332177de8 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs @@ -1,5 +1,4 @@ -using Discord.Net.Rest; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; #if DEBUG_LIMITS @@ -88,7 +87,10 @@ namespace Discord.Net.Queue #if DEBUG_LIMITS Debug.WriteLine($"[{id}] (!) 502"); #endif - continue; //Continue + if ((request.Options.RetryMode & RetryMode.Retry502) == 0) + throw new HttpException(HttpStatusCode.BadGateway, null); + + continue; //Retry default: string reason = null; if (response.Stream != null) @@ -115,13 +117,28 @@ namespace Discord.Net.Queue return response.Stream; } } + catch (TimeoutException) + { #if DEBUG_LIMITS - catch + Debug.WriteLine($"[{id}] Timeout"); +#endif + if ((request.Options.RetryMode & RetryMode.RetryTimeouts) == 0) + throw; + + await Task.Delay(500); + continue; //Retry + } + catch (Exception) { +#if DEBUG_LIMITS Debug.WriteLine($"[{id}] Error"); - throw; - } #endif + if ((request.Options.RetryMode & RetryMode.RetryErrors) == 0) + throw; + + await Task.Delay(500); + continue; //Retry + } finally { UpdateRateLimit(id, request, info, lag, false); @@ -140,7 +157,7 @@ namespace Discord.Net.Queue while (true) { - if (DateTimeOffset.UtcNow > request.TimeoutAt || request.CancelToken.IsCancellationRequested) + if (DateTimeOffset.UtcNow > request.TimeoutAt || request.Options.CancelToken.IsCancellationRequested) { if (!isRateLimited) throw new TimeoutException(); @@ -162,6 +179,10 @@ namespace Discord.Net.Queue isRateLimited = true; await _queue.RaiseRateLimitTriggered(Id, null).ConfigureAwait(false); } + + if ((request.Options.RetryMode & RetryMode.RetryRatelimit) == 0) + throw new RateLimitedException(); + if (resetAt.HasValue) { if (resetAt > timeoutAt) @@ -171,7 +192,7 @@ namespace Discord.Net.Queue Debug.WriteLine($"[{id}] Sleeping {millis} ms (Pre-emptive)"); #endif if (millis > 0) - await Task.Delay(millis, request.CancelToken).ConfigureAwait(false); + await Task.Delay(millis, request.Options.CancelToken).ConfigureAwait(false); } else { @@ -180,7 +201,7 @@ namespace Discord.Net.Queue #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Sleeping 500* ms (Pre-emptive)"); #endif - await Task.Delay(500, request.CancelToken).ConfigureAwait(false); + await Task.Delay(500, request.Options.CancelToken).ConfigureAwait(false); } continue; } diff --git a/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs index 75869d52a..83c5e0eb5 100644 --- a/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs @@ -15,7 +15,7 @@ namespace Discord.Net.Queue public override async Task SendAsync() { - return await Client.SendAsync(Method, Endpoint, Json, CancelToken, Options.HeaderOnly).ConfigureAwait(false); + return await Client.SendAsync(Method, Endpoint, Json, Options.CancelToken, Options.HeaderOnly).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs index d132ef395..424a5325e 100644 --- a/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs @@ -16,7 +16,7 @@ namespace Discord.Net.Queue public override async Task SendAsync() { - return await Client.SendAsync(Method, Endpoint, MultipartParams, CancelToken, Options.HeaderOnly).ConfigureAwait(false); + return await Client.SendAsync(Method, Endpoint, MultipartParams, Options.CancelToken, Options.HeaderOnly).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs b/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs index 5d5bc1e59..7f358e786 100644 --- a/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs +++ b/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs @@ -1,7 +1,6 @@ using Discord.Net.Rest; using System; using System.IO; -using System.Threading; using System.Threading.Tasks; namespace Discord.Net.Queue @@ -14,7 +13,6 @@ namespace Discord.Net.Queue public DateTimeOffset? TimeoutAt { get; } public TaskCompletionSource Promise { get; } public RequestOptions Options { get; } - public CancellationToken CancelToken { get; internal set; } public RestRequest(IRestClient client, string method, string endpoint, RequestOptions options) { @@ -24,14 +22,13 @@ namespace Discord.Net.Queue Method = method; Endpoint = endpoint; Options = options; - CancelToken = CancellationToken.None; TimeoutAt = options.Timeout.HasValue ? DateTimeOffset.UtcNow.AddMilliseconds(options.Timeout.Value) : (DateTimeOffset?)null; Promise = new TaskCompletionSource(); } public virtual async Task SendAsync() { - return await Client.SendAsync(Method, Endpoint, CancelToken, Options.HeaderOnly).ConfigureAwait(false); + return await Client.SendAsync(Method, Endpoint, Options.CancelToken, Options.HeaderOnly).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.Core/RequestOptions.cs b/src/Discord.Net.Core/RequestOptions.cs index b82ec29c8..4f5910c53 100644 --- a/src/Discord.Net.Core/RequestOptions.cs +++ b/src/Discord.Net.Core/RequestOptions.cs @@ -1,11 +1,18 @@ -namespace Discord +using System.Threading; + +namespace Discord { public class RequestOptions { public static RequestOptions Default => new RequestOptions(); - /// The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. + /// + /// The max time, in milliseconds, to wait for this request to complete. If null, a request will not time out. + /// If a rate limit has been triggered for this request's bucket and will not be unpaused in time, this request will fail immediately. + /// public int? Timeout { get; set; } + public CancellationToken CancelToken { get; set; } = CancellationToken.None; + public RetryMode? RetryMode { get; set; } public bool HeaderOnly { get; internal set; } internal bool IgnoreState { get; set; } @@ -13,7 +20,7 @@ internal bool IsClientBucket { get; set; } internal static RequestOptions CreateOrClone(RequestOptions options) - { + { if (options == null) return new RequestOptions(); else diff --git a/src/Discord.Net.Core/RetryMode.cs b/src/Discord.Net.Core/RetryMode.cs new file mode 100644 index 000000000..9dccfc313 --- /dev/null +++ b/src/Discord.Net.Core/RetryMode.cs @@ -0,0 +1,22 @@ +using System; + +namespace Discord +{ + /// Specifies how a request should act in the case of an error. + [Flags] + public enum RetryMode + { + /// If a request fails, an exception is thrown immediately. + AlwaysFail = 0x0, + /// Retry if a request timed out. + RetryTimeouts = 0x1, + /// Retry if a request failed due to a network error. + RetryErrors = 0x2, + /// Retry if a request failed due to a ratelimit. + RetryRatelimit = 0x4, + /// Retry if a request failed due to an HTTP error 502. + Retry502 = 0x8, + /// Continuously retry a request until it times out, its cancel token is triggered, or the server responds with a non-502 error. + AlwaysRetry = RetryTimeouts | RetryErrors | RetryRatelimit | Retry502, + } +} diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs index 720c975c0..ee6ad84e1 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs @@ -69,15 +69,13 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } public DiscordRpcApiClient(string clientId, string userAgent, string origin, RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, - JsonSerializer serializer = null, RequestQueue requestQueue = null) - : base(restClientProvider, userAgent, serializer, requestQueue) + RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, RequestQueue requestQueue = null) + : base(restClientProvider, userAgent, defaultRetryMode, serializer, requestQueue, false) { _connectionLock = new SemaphoreSlim(1, 1); _clientId = clientId; _origin = origin; - FetchCurrentUser = false; - _requestQueue = requestQueue ?? new RequestQueue(); _requests = new ConcurrentDictionary(); diff --git a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs index f0dd5f852..9592e2a04 100644 --- a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs @@ -32,11 +32,12 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } - public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider, JsonSerializer serializer = null, RequestQueue requestQueue = null) - : base(restClientProvider, userAgent, serializer, requestQueue) + public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider, + RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, RequestQueue requestQueue = null) + : base(restClientProvider, userAgent, defaultRetryMode, serializer, requestQueue, true) { _gatewayClient = webSocketProvider(); - //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+) + //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) _gatewayClient.BinaryMessage += async (data, index, count) => { using (var compressed = new MemoryStream(data, index + 2, count - 2)) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index a150a6d15..2f952bdaa 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -221,7 +221,5 @@ namespace Discord.WebSocket remove { _recipientRemovedEvent.Remove(value); } } private readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>(); - - //TODO: Add PresenceUpdated? VoiceStateUpdated?, VoiceConnected, VoiceDisconnected; } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index fde2d100b..0faa3cbbd 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -130,7 +130,7 @@ namespace Discord.WebSocket protected override async Task OnLoginAsync(TokenType tokenType, string token) { - var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true}).ConfigureAwait(false); + var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); } protected override async Task OnLogoutAsync() From 2dd62ba5e70d34d5c05d89b7e2944a67597267ff Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 09:15:32 -0400 Subject: [PATCH 089/263] Set DefaultRunMode to Sync --- src/Discord.Net.Commands/CommandServiceConfig.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 4ac79fe8f..8377d4e60 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -3,7 +3,7 @@ public class CommandServiceConfig { /// The default RunMode commands should have, if one is not specified on the Command attribute or builder. - public RunMode DefaultRunMode { get; set; } = RunMode.Mixed; + public RunMode DefaultRunMode { get; set; } = RunMode.Sync; /// Should commands be case-sensitive? public bool CaseSensitiveCommands { get; set; } = false; } From d623b18dff389250a9981bc378a5647319d00288 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 10:00:39 -0400 Subject: [PATCH 090/263] Added AddParameter. Fixed AddParameter ignoring default typereaders. --- src/Discord.Net.Commands/Builders/CommandBuilder.cs | 7 +++++++ src/Discord.Net.Commands/Builders/ParameterBuilder.cs | 11 +++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index 9b983fd1f..a3f2fee78 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -80,6 +80,13 @@ namespace Discord.Commands.Builders _preconditions.Add(precondition); return this; } + public CommandBuilder AddParameter(string name, Action createFunc) + { + var param = new ParameterBuilder(this, name, typeof(T)); + createFunc(param); + _parameters.Add(param); + return this; + } public CommandBuilder AddParameter(string name, Type type, Action createFunc) { var param = new ParameterBuilder(this, name, type); diff --git a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs index d4cf598ec..c9801f458 100644 --- a/src/Discord.Net.Commands/Builders/ParameterBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ParameterBuilder.cs @@ -43,10 +43,13 @@ namespace Discord.Commands.Builders internal void SetType(Type type) { var readers = Command.Module.Service.GetTypeReaders(type); - if (readers == null) - throw new InvalidOperationException($"{type} does not have a TypeReader registered for it"); - - TypeReader = readers.FirstOrDefault().Value; + if (readers != null) + TypeReader = readers.FirstOrDefault().Value; + else + TypeReader = Command.Module.Service.GetDefaultTypeReader(type); + + if (TypeReader == null) + throw new InvalidOperationException($"{type} does not have a TypeReader registered for it"); if (type.GetTypeInfo().IsValueType) DefaultValue = Activator.CreateInstance(type); From 86d9f524384d2445adcb91b9456b0b49346c98c5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 11:11:07 -0400 Subject: [PATCH 091/263] Cleaned up new params --- src/Discord.Net.Core/API/Image.cs | 9 +++- .../API/Rest/ModifyCurrentUserParams.cs | 2 +- .../Channels/ModifyGuildChannelsParams.cs | 6 +++ .../Entities/Guilds/ModifyGuildEmbedParams.cs | 4 ++ .../Entities/Guilds/ModifyGuildParams.cs | 12 +++++ src/Discord.Net.Core/Entities/Image.cs | 2 + .../Entities/Roles/ModifyGuildRolesParams.cs | 3 ++ .../Entities/Users/ModifyGuildMemberParams.cs | 14 ++++- .../Entities/Guilds/GuildHelper.cs | 52 +++++++++++++------ .../Entities/Users/RestGuildUser.cs | 15 ++++-- .../Entities/Users/UserHelper.cs | 15 ++++-- 11 files changed, 106 insertions(+), 28 deletions(-) diff --git a/src/Discord.Net.Core/API/Image.cs b/src/Discord.Net.Core/API/Image.cs index 44c58f344..a500ebfd5 100644 --- a/src/Discord.Net.Core/API/Image.cs +++ b/src/Discord.Net.Core/API/Image.cs @@ -18,9 +18,16 @@ namespace Discord.API Hash = hash; } - public static Image Create(Discord.Image image) + internal static Image Create(Discord.Image image) { return new Image(image.Stream); } + internal static Image? Create(Discord.Image? image) + { + if (image.HasValue) + return new Image(image.Value.Stream); + else + return null; + } } } diff --git a/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs index d11ef2b77..3f7afe187 100644 --- a/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs @@ -9,6 +9,6 @@ namespace Discord.API.Rest [JsonProperty("username")] public Optional Username { get; set; } [JsonProperty("avatar")] - public Optional Avatar { get; set; } + public Optional Avatar { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs index e46e172b9..14b77457f 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs @@ -2,7 +2,13 @@ { public class ModifyGuildChannelsParams { + /// + /// The id of the channel to apply this position to. + /// public ulong Id { get; set; } + /// + /// The new zero-based position of this channel. + /// public int Position { get; set; } public ModifyGuildChannelsParams(ulong id, int position) diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs index 11b72774c..f5d212e17 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs @@ -12,6 +12,10 @@ /// /// What channel should the invite place users in, if not null. /// + public Optional Channel { get; set; } + /// + /// What channel should the invite place users in, if not null. + /// public Optional ChannelId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs index 08deff727..ec7866b8a 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs @@ -21,6 +21,10 @@ /// public Optional Name { get; set; } /// + /// The region for the Guild's voice connections + /// + public Optional Region { get; set; } + /// /// The ID of the region for the Guild's voice connections /// public Optional RegionId { get; set; } @@ -48,10 +52,18 @@ /// public Optional Splash { get; set; } /// + /// The IVoiceChannel where AFK users should be sent. + /// + public Optional AfkChannel { get; set; } + /// /// The ID of the IVoiceChannel where AFK users should be sent. /// public Optional AfkChannelId { get; set; } /// + /// The owner of this guild. + /// + public Optional Owner { get; set; } + /// /// The ID of the owner of this guild. /// public Optional OwnerId { get; set; } diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index 538d82730..e92342e7a 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -17,6 +17,7 @@ namespace Discord { Stream = stream; } +#if NETSTANDARD1_3 /// /// Create the image from a file path. /// @@ -28,5 +29,6 @@ namespace Discord { Stream = File.OpenRead(path); } +#endif } } diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs index 4d6a0f63b..5140f4dae 100644 --- a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs +++ b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs @@ -2,6 +2,9 @@ { public class ModifyGuildRolesParams : ModifyGuildRoleParams { + /// + /// The id of the role to be edited + /// public ulong Id { get; } public ModifyGuildRolesParams(ulong id) diff --git a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs index 57909cd03..50f535a4c 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs +++ b/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs @@ -1,4 +1,6 @@ -namespace Discord +using System.Collections.Generic; + +namespace Discord { /// /// Modify an IGuildUser with the following parameters. @@ -42,7 +44,15 @@ /// To add a role to a user: /// To remove a role from a user: /// - public Optional Roles { get; set; } + public Optional> Roles { get; set; } + /// + /// What roles should the user have? + /// + /// + /// To add a role to a user: + /// To remove a role from a user: + /// + public Optional> RoleIds { get; set; } /// /// Move a user to a voice channel. /// diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 428541509..5c6f1cb0e 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -27,16 +27,31 @@ namespace Discord.Rest AfkChannelId = args.AfkChannelId, AfkTimeout = args.AfkTimeout, DefaultMessageNotifications = args.DefaultMessageNotifications, + Icon = args.Icon.IsSpecified ? ImageModel.Create(args.Icon.Value) : Optional.Create(), Name = args.Name, - OwnerId = args.OwnerId, - RegionId = args.RegionId, + Splash = args.Splash.IsSpecified ? ImageModel.Create(args.Splash.Value) : Optional.Create(), Username = args.Username, VerificationLevel = args.VerificationLevel }; - if (apiArgs.Splash.IsSpecified && guild.SplashId != null) + if (args.AfkChannel.IsSpecified) + apiArgs.AfkChannelId = args.AfkChannel.Value.Id; + else if (args.AfkChannelId.IsSpecified) + apiArgs.AfkChannelId = args.AfkChannelId.Value; + + if (args.Owner.IsSpecified) + apiArgs.OwnerId = args.Owner.Value.Id; + else if (args.OwnerId.IsSpecified) + apiArgs.OwnerId = args.OwnerId.Value; + + if (args.Region.IsSpecified) + apiArgs.RegionId = args.Region.Value.Id; + else if (args.RegionId.IsSpecified) + apiArgs.RegionId = args.RegionId.Value; + + if (!apiArgs.Splash.IsSpecified && guild.SplashId != null) apiArgs.Splash = new ImageModel(guild.SplashId); - if (apiArgs.Icon.IsSpecified && guild.IconId != null) + if (!apiArgs.Icon.IsSpecified && guild.IconId != null) apiArgs.Icon = new ImageModel(guild.IconId); return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); @@ -50,9 +65,14 @@ namespace Discord.Rest func(args); var apiArgs = new API.Rest.ModifyGuildEmbedParams { - ChannelId = args.ChannelId, Enabled = args.Enabled }; + + if (args.Channel.IsSpecified) + apiArgs.ChannelId = args.Channel.Value?.Id; + else if (args.ChannelId.IsSpecified) + apiArgs.ChannelId = args.ChannelId.Value; + return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client, @@ -74,32 +94,32 @@ namespace Discord.Rest }); return await client.ApiClient.ModifyGuildRolesAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } - public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client, + public static async Task LeaveAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { await client.ApiClient.LeaveGuildAsync(guild.Id, options).ConfigureAwait(false); } - public static async Task DeleteAsync(IGuild guild, BaseDiscordClient client, + public static async Task DeleteAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { await client.ApiClient.DeleteGuildAsync(guild.Id, options).ConfigureAwait(false); } //Bans - public static async Task> GetBansAsync(IGuild guild, BaseDiscordClient client, + public static async Task> GetBansAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { var models = await client.ApiClient.GetGuildBansAsync(guild.Id, options).ConfigureAwait(false); return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); } - - public static async Task AddBanAsync(IGuild guild, BaseDiscordClient client, + + public static async Task AddBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, int pruneDays, RequestOptions options) { var args = new CreateGuildBanParams { DeleteMessageDays = pruneDays }; await client.ApiClient.CreateGuildBanAsync(guild.Id, userId, args, options).ConfigureAwait(false); - } - public static async Task RemoveBanAsync(IGuild guild, BaseDiscordClient client, + } + public static async Task RemoveBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options) { await client.ApiClient.RemoveGuildBanAsync(guild.Id, userId, options).ConfigureAwait(false); @@ -114,7 +134,7 @@ namespace Discord.Rest return RestGuildChannel.Create(client, guild, model); return null; } - public static async Task> GetChannelsAsync(IGuild guild, BaseDiscordClient client, + public static async Task> GetChannelsAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { var models = await client.ApiClient.GetGuildChannelsAsync(guild.Id, options).ConfigureAwait(false); @@ -140,7 +160,7 @@ namespace Discord.Rest } //Integrations - public static async Task> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client, + public static async Task> GetIntegrationsAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { var models = await client.ApiClient.GetGuildIntegrationsAsync(guild.Id, options).ConfigureAwait(false); @@ -155,7 +175,7 @@ namespace Discord.Rest } //Invites - public static async Task> GetInvitesAsync(IGuild guild, BaseDiscordClient client, + public static async Task> GetInvitesAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { var models = await client.ApiClient.GetGuildInvitesAsync(guild.Id, options).ConfigureAwait(false); @@ -191,7 +211,7 @@ namespace Discord.Rest return RestGuildUser.Create(client, guild, model); return null; } - public static async Task GetCurrentUserAsync(IGuild guild, BaseDiscordClient client, + public static async Task GetCurrentUserAsync(IGuild guild, BaseDiscordClient client, RequestOptions options) { return await GetUserAsync(guild, client, client.CurrentUser.Id, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index 180ad38bc..40d57c511 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Model = Discord.API.GuildMember; @@ -30,7 +31,7 @@ namespace Discord.Rest } } public IReadOnlyCollection RoleIds => _roleIds; - + public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); internal RestGuildUser(BaseDiscordClient discord, IGuild guild, ulong id) @@ -61,21 +62,25 @@ namespace Discord.Rest roles.Add(roleIds[i]); _roleIds = roles.ToImmutable(); } - + public override async Task UpdateAsync(RequestOptions options = null) { var model = await Discord.ApiClient.GetGuildMemberAsync(GuildId, Id, options).ConfigureAwait(false); Update(model); } public async Task ModifyAsync(Action func, RequestOptions options = null) - { + { var args = await UserHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); if (args.Deaf.IsSpecified) IsDeafened = args.Deaf.Value; if (args.Mute.IsSpecified) IsMuted = args.Mute.Value; - if (args.RoleIds.IsSpecified) - UpdateRoles(args.RoleIds.Value); + if (args.Nickname.IsSpecified) + Nickname = args.Nickname.Value; + if (args.Roles.IsSpecified) + UpdateRoles(args.Roles.Value.Select(x => x.Id).ToArray()); + else if (args.RoleIds.IsSpecified) + UpdateRoles(args.RoleIds.Value.ToArray()); } public Task KickAsync(RequestOptions options = null) => UserHelper.KickAsync(this, Discord, options); diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index dbfa2f2e5..e23c1cbb0 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -16,9 +16,13 @@ namespace Discord.Rest func(args); var apiArgs = new API.Rest.ModifyCurrentUserParams { - Avatar = args.Avatar.IsSpecified ? ImageModel.Create(args.Avatar.Value) : Optional.Create(), + Avatar = args.Avatar.IsSpecified ? ImageModel.Create(args.Avatar.Value) : Optional.Create(), Username = args.Username }; + + if (!apiArgs.Avatar.IsSpecified && user.AvatarId != null) + apiArgs.Avatar = new ImageModel(user.AvatarId); + return await client.ApiClient.ModifySelfAsync(apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action func, @@ -31,9 +35,14 @@ namespace Discord.Rest ChannelId = args.Channel.IsSpecified ? args.Channel.Value.Id : Optional.Create(), Deaf = args.Deaf, Mute = args.Mute, - Nickname = args.Nickname, - RoleIds = args.Roles.IsSpecified ? args.Roles.Value.Select(r => r.Id).ToArray() : Optional.Create(), + Nickname = args.Nickname }; + + if (args.Roles.IsSpecified) + apiArgs.RoleIds = args.Roles.Value.Select(x => x.Id).ToArray(); + else if (args.RoleIds.IsSpecified) + apiArgs.RoleIds = args.RoleIds.Value.ToArray(); + await client.ApiClient.ModifyGuildMemberAsync(user.GuildId, user.Id, apiArgs, options).ConfigureAwait(false); return args; } From ded721be039c778f4ba17019190558566f600a6d Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 16 Dec 2016 15:32:49 -0400 Subject: [PATCH 092/263] Fixed xmldoc --- src/Discord.Net.Core/Entities/Image.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index e92342e7a..f9a65ae4c 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -12,7 +12,6 @@ namespace Discord /// Create the image with a Stream. /// /// This must be some type of stream with the contents of a file in it. - /// public Image(Stream stream) { Stream = stream; From b9b6ac36feeae53576f0d534d32b5c7b3f48920f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Fri, 16 Dec 2016 19:18:27 -0500 Subject: [PATCH 093/263] Add docstrings, per volt's feedback --- .../Attributes/InjectAttribute.cs | 6 +++ .../Dependencies/DependencyMap.cs | 10 ++++- .../Dependencies/IDependencyMap.cs | 44 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs index 836a4c668..09c0b553b 100644 --- a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs @@ -2,6 +2,12 @@ namespace Discord.Commands { + /// + /// Indicates that this property should be filled in by dependency injection. + /// + /// + /// This property **MUST** have a setter. + /// [AttributeUsage(AttributeTargets.Property)] public sealed class InjectAttribute : Attribute { diff --git a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs index 7f489fda0..0761f5fff 100644 --- a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs @@ -14,14 +14,18 @@ namespace Discord.Commands map = new Dictionary>(); } + /// public void Add(T obj) where T : class => AddFactory(() => obj); + /// public void AddTransient() where T : class, new() => AddFactory(() => new T()); + /// public void AddTransient() where TKey : class where TImpl : class, TKey, new() => AddFactory(() => new TImpl()); - + + /// public void AddFactory(Func factory) where T : class { var t = typeof(T); @@ -30,10 +34,12 @@ namespace Discord.Commands map.Add(t, factory); } + /// public T Get() { return (T)Get(typeof(T)); } + /// public object Get(Type t) { object result; @@ -43,6 +49,7 @@ namespace Discord.Commands return result; } + /// public bool TryGet(out T result) { object untypedResult; @@ -57,6 +64,7 @@ namespace Discord.Commands return false; } } + /// public bool TryGet(Type t, out object result) { Func func; diff --git a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs index aab94156b..fb042c304 100644 --- a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs @@ -4,15 +4,59 @@ namespace Discord.Commands { public interface IDependencyMap { + /// + /// Add an instance of a service to be injected. + /// + /// The type of service. + /// The instance of a service. void Add(T obj) where T : class; + /// + /// Add a service that will be injected by a new instance every time. + /// + /// The type of instance to inject. void AddTransient() where T : class, new(); + /// + /// Add a service that will be injected by a new instance every time. + /// + /// The type to look for when injecting. + /// The type to inject when injecting. + /// + /// map.AddTransient<IService, Service> + /// void AddTransient() where TKey: class where TImpl : class, TKey, new(); + /// + /// Add a service that will be injected by a factory. + /// + /// The type to look for when injecting. + /// The factory that returns a type of this service. void AddFactory(Func factory) where T : class; + /// + /// Pull an object from the map. + /// + /// The type of service. + /// An instance of this service. T Get(); + /// + /// Try to pull an object from the map. + /// + /// The type of service. + /// The instance of this service. + /// Whether or not this object could be found in the map. bool TryGet(out T result); + /// + /// Pull an object from the map. + /// + /// The type of service. + /// An instance of this service. object Get(Type t); + /// + /// Try to pull an object from the map. + /// + /// The type of service. + /// An instance of this service. + /// Whether or not this object could be found in the map. bool TryGet(Type t, out object result); } } From 604d69fb7fc36672779bf55c18ceaba0ecb0590b Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 17 Dec 2016 16:34:04 -0500 Subject: [PATCH 094/263] Revert "Implement configurable command node separators" --- src/Discord.Net.Commands/CommandService.cs | 8 +-- .../CommandServiceConfig.cs | 2 - src/Discord.Net.Commands/Info/CommandInfo.cs | 2 +- src/Discord.Net.Commands/Info/ModuleInfo.cs | 8 +-- src/Discord.Net.Commands/Map/CommandMap.cs | 12 ++-- .../Map/CommandMapNode.cs | 64 ++++++------------- 6 files changed, 33 insertions(+), 63 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index c15fda334..010a0ee8a 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -22,7 +22,6 @@ namespace Discord.Commands internal readonly bool _caseSensitive; internal readonly RunMode _defaultRunMode; - internal readonly char _splitCharacter; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -61,7 +60,6 @@ namespace Discord.Commands _caseSensitive = config.CaseSensitiveCommands; _defaultRunMode = config.DefaultRunMode; - _splitCharacter = config.CommandSplitCharacter; } //Modules @@ -131,7 +129,7 @@ namespace Discord.Commands _moduleDefs.Add(module); foreach (var command in module.Commands) - _map.AddCommand(command, this); + _map.AddCommand(command); foreach (var submodule in module.Submodules) LoadModuleInternal(submodule); @@ -175,7 +173,7 @@ namespace Discord.Commands return false; foreach (var cmd in module.Commands) - _map.RemoveCommand(cmd, this); + _map.RemoveCommand(cmd); foreach (var submodule in module.Submodules) { @@ -216,7 +214,7 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); - var matches = _map.GetCommands(searchInput, this).OrderByDescending(x => x.Priority).ToImmutableArray(); + var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Priority).ToImmutableArray(); if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 7cba56515..8377d4e60 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -6,7 +6,5 @@ public RunMode DefaultRunMode { get; set; } = RunMode.Sync; /// Should commands be case-sensitive? public bool CaseSensitiveCommands { get; set; } = false; - /// The character which splits commands - public char CommandSplitCharacter { get; set; } = ' '; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 1e41a0aa9..a6ac50005 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -44,7 +44,7 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + service._splitCharacter + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); + Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); // only module provides aliases else if (module.Aliases.Count > 0) Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 65417e3fd..ab4f65713 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -30,14 +30,14 @@ namespace Discord.Commands Remarks = builder.Remarks; Parent = parent; - Aliases = BuildAliases(builder, service).ToImmutableArray(); + Aliases = BuildAliases(builder).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)); Preconditions = BuildPreconditions(builder).ToImmutableArray(); Submodules = BuildSubmodules(builder, service).ToImmutableArray(); } - private static IEnumerable BuildAliases(ModuleBuilder builder, CommandService service) + private static IEnumerable BuildAliases(ModuleBuilder builder) { IEnumerable result = null; @@ -60,9 +60,9 @@ namespace Discord.Commands result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly } else if (result.Count() > level.Aliases.Count) - result = result.Permutate(level.Aliases, (first, second) => first + service._splitCharacter + second); + result = result.Permutate(level.Aliases, (first, second) => first + " " + second); else - result = level.Aliases.Permutate(result, (second, first) => first + service._splitCharacter + second); + result = level.Aliases.Permutate(result, (second, first) => first + " " + second); } if (result == null) //there were no aliases; default to an empty list diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs index 2478f488d..3a5239878 100644 --- a/src/Discord.Net.Commands/Map/CommandMap.cs +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -12,20 +12,20 @@ namespace Discord.Commands _root = new CommandMapNode(""); } - public void AddCommand(CommandInfo command, CommandService service) + public void AddCommand(CommandInfo command) { foreach (string text in GetAliases(command)) - _root.AddCommand(service, text, 0, command); + _root.AddCommand(text, 0, command); } - public void RemoveCommand(CommandInfo command, CommandService service) + public void RemoveCommand(CommandInfo command) { foreach (string text in GetAliases(command)) - _root.RemoveCommand(service, text, 0, command); + _root.RemoveCommand(text, 0, command); } - public IEnumerable GetCommands(string text, CommandService service) + public IEnumerable GetCommands(string text) { - return _root.GetCommands(service, text, 0); + return _root.GetCommands(text, 0); } private IReadOnlyList GetAliases(CommandInfo command) diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs index c1365f963..a86c0643d 100644 --- a/src/Discord.Net.Commands/Map/CommandMapNode.cs +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -23,9 +23,9 @@ namespace Discord.Commands _commands = ImmutableArray.Create(); } - public void AddCommand(CommandService service, string text, int index, CommandInfo command) + public void AddCommand(string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(service, text, index); + int nextSpace = NextWhitespace(text, index); string name; lock (_lockObj) @@ -44,13 +44,13 @@ namespace Discord.Commands name = text.Substring(index, nextSpace - index); var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); - nextNode.AddCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); } } } - public void RemoveCommand(CommandService service, string text, int index, CommandInfo command) + public void RemoveCommand(string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(service, text, index); + int nextSpace = NextWhitespace(text, index); string name; lock (_lockObj) @@ -67,7 +67,7 @@ namespace Discord.Commands CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - nextNode.RemoveCommand(service, nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); if (nextNode.IsEmpty) _nodes.TryRemove(name, out nextNode); } @@ -75,57 +75,32 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(CommandService service, string text, int index) + public IEnumerable GetCommands(string text, int index) { - int nextCommand = NextCommandSegment(service, text, index); - string name = null; + int nextSpace = NextWhitespace(text, index); + string name; - //got all command segments or base-level command - if (nextCommand == -1) - { - var commands = _commands; - for (int i = 0; i < commands.Length; i++) - yield return _commands[i]; + var commands = _commands; + for (int i = 0; i < commands.Length; i++) + yield return _commands[i]; - //are we a base-level command? - int nextSpace = NextWhitespace(service, text, index); - if (nextSpace != -1) - { - name = text.Substring(index, nextSpace - index); - } - else - { - name = text.Substring(index); - } - } - else + if (text != "") { - name = text.Substring(index, nextCommand - index); - } + if (nextSpace == -1) + name = text.Substring(index); + else + name = text.Substring(index, nextSpace - index); - if (name != null) - { CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - foreach (var cmd in nextNode.GetCommands(service, text, nextCommand + 1)) + foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1)) yield return cmd; } } } - private static int NextCommandSegment(CommandService service, string text, int startIndex) - { - int lowest = int.MaxValue; - - int index = text.IndexOf(service._splitCharacter, startIndex); - if (index != -1 && index < lowest) - lowest = index; - - return (lowest != int.MaxValue) ? lowest : -1; - } - - private static int NextWhitespace(CommandService service, string text, int startIndex) + private static int NextWhitespace(string text, int startIndex) { int lowest = int.MaxValue; for (int i = 0; i < _whitespaceChars.Length; i++) @@ -134,7 +109,6 @@ namespace Discord.Commands if (index != -1 && index < lowest) lowest = index; } - return (lowest != int.MaxValue) ? lowest : -1; } } From 6676a5465572ea317aed6d6285c9a9f4ce1103bb Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 18 Dec 2016 13:58:51 -0400 Subject: [PATCH 095/263] IAudioChannel should extend IChannel --- src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs index 9b8074efb..6c9507299 100644 --- a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs @@ -1,6 +1,6 @@ namespace Discord { - public interface IAudioChannel + public interface IAudioChannel : IChannel { } } From 0d209b3fdbfd732a2e74c5f3faa3191fbded2e1f Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 18 Dec 2016 14:18:33 -0400 Subject: [PATCH 096/263] Added internal error for if a matching alias cannot be found --- src/Discord.Net.Commands/Info/CommandInfo.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index a6ac50005..c24d35f6a 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -93,15 +93,18 @@ namespace Discord.Commands return ParseResult.FromError(preconditionResult.Value); string input = searchResult.Text; - var matchingAliases = Aliases.Where(alias => input.StartsWith(alias)); - - string matchingAlias = ""; + var matchingAliases = Aliases.Where(alias => input.StartsWith(alias)).ToArray(); + + string matchingAlias = null; foreach (string alias in matchingAliases) { if (alias.Length > matchingAlias.Length) matchingAlias = alias; } - + + if (matchingAlias == null) + return ParseResult.FromError(CommandError.ParseFailed, "Unable to find matching alias"); + input = input.Substring(matchingAlias.Length); return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false); From 2e2fbaa5f3101407e6d9e1ae515a2931f687e653 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sun, 18 Dec 2016 17:23:41 +0000 Subject: [PATCH 097/263] Add mentionable property to ModifyGuildRoleParams Allows roles to be configured to be mentionable, fixes #424 --- src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs | 2 ++ .../Entities/Roles/ModifyGuildRoleParams.cs | 7 +++++++ src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs | 1 + 3 files changed, 10 insertions(+) diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs index d1226b534..f004b201e 100644 --- a/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs @@ -16,5 +16,7 @@ namespace Discord.API.Rest public Optional Color { get; set; } [JsonProperty("hoist")] public Optional Hoist { get; set; } + [JsonProperty("mentionable")] + public Optional Mentionable { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs index fa99c5bcc..bdd1c41e9 100644 --- a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs @@ -47,5 +47,12 @@ /// If this role is the EveryoneRole, this value may not be set. /// public Optional Hoist { get; set; } + /// + /// Whether or not this role can be mentioned. + /// + /// + /// If this role is the EveryoneRole, this value may not be set. + /// + public Optional Mentionable { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index 4b786b6e6..07836c993 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -22,6 +22,7 @@ namespace Discord.Rest { Color = args.Color.IsSpecified ? args.Color.Value.RawValue : Optional.Create(), Hoist = args.Hoist, + Mentionable = args.Mentionable, Name = args.Name, Permissions = args.Permissions.IsSpecified ? args.Permissions.Value.RawValue : Optional.Create(), Position = args.Position From 5663e47db6b1b17d8c34d8f6333220aeead8f3a6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 18 Dec 2016 17:48:33 -0400 Subject: [PATCH 098/263] Added command separator char, improved passing search results to parser --- src/Discord.Net.Commands/CommandMatch.cs | 26 +++++++ src/Discord.Net.Commands/CommandService.cs | 14 ++-- .../CommandServiceConfig.cs | 2 + src/Discord.Net.Commands/Info/CommandInfo.cs | 29 +++---- src/Discord.Net.Commands/Map/CommandMap.cs | 24 +++--- .../Map/CommandMapNode.cs | 76 ++++++++++++------- .../Results/SearchResult.cs | 6 +- 7 files changed, 106 insertions(+), 71 deletions(-) create mode 100644 src/Discord.Net.Commands/CommandMatch.cs diff --git a/src/Discord.Net.Commands/CommandMatch.cs b/src/Discord.Net.Commands/CommandMatch.cs new file mode 100644 index 000000000..2e13e0f46 --- /dev/null +++ b/src/Discord.Net.Commands/CommandMatch.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + public struct CommandMatch + { + public CommandInfo Command { get; } + public string Alias { get; } + + public CommandMatch(CommandInfo command, string alias) + { + Command = command; + Alias = alias; + } + + public Task CheckPreconditionsAsync(CommandContext context, IDependencyMap map = null) + => Command.CheckPreconditionsAsync(context, map); + public Task ParseAsync(CommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null) + => Command.ParseAsync(context, Alias.Length, searchResult, preconditionResult); + public Task ExecuteAsync(CommandContext context, IEnumerable argList, IEnumerable paramList, IDependencyMap map) + => Command.ExecuteAsync(context, argList, paramList, map); + public Task ExecuteAsync(CommandContext context, ParseResult parseResult, IDependencyMap map) + => Command.ExecuteAsync(context, parseResult, map); + } +} diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 010a0ee8a..7152b87da 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -21,6 +21,7 @@ namespace Discord.Commands private readonly CommandMap _map; internal readonly bool _caseSensitive; + internal readonly char _separatorChar; internal readonly RunMode _defaultRunMode; public IEnumerable Modules => _moduleDefs.Select(x => x); @@ -30,10 +31,14 @@ namespace Discord.Commands public CommandService() : this(new CommandServiceConfig()) { } public CommandService(CommandServiceConfig config) { + _caseSensitive = config.CaseSensitiveCommands; + _separatorChar = config.SeparatorChar; + _defaultRunMode = config.DefaultRunMode; + _moduleLock = new SemaphoreSlim(1, 1); _typedModuleDefs = new ConcurrentDictionary(); _moduleDefs = new ConcurrentBag(); - _map = new CommandMap(); + _map = new CommandMap(this); _typeReaders = new ConcurrentDictionary>(); _defaultTypeReaders = new ConcurrentDictionary @@ -57,9 +62,6 @@ namespace Discord.Commands }; foreach (var type in PrimitiveParsers.SupportedTypes) _defaultTypeReaders[type] = PrimitiveTypeReader.Create(type); - - _caseSensitive = config.CaseSensitiveCommands; - _defaultRunMode = config.DefaultRunMode; } //Modules @@ -214,7 +216,7 @@ namespace Discord.Commands public SearchResult Search(CommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); - var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Priority).ToImmutableArray(); + var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Command.Priority).ToImmutableArray(); if (matches.Length > 0) return SearchResult.FromSuccess(input, matches); @@ -269,7 +271,7 @@ namespace Discord.Commands } } - return await commands[i].Execute(context, parseResult, dependencyMap).ConfigureAwait(false); + return await commands[i].ExecuteAsync(context, parseResult, dependencyMap).ConfigureAwait(false); } return SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload."); diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 8377d4e60..037e315c7 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -4,6 +4,8 @@ { /// The default RunMode commands should have, if one is not specified on the Command attribute or builder. public RunMode DefaultRunMode { get; set; } = RunMode.Sync; + + public char SeparatorChar { get; set; } = ' '; /// Should commands be case-sensitive? public bool CaseSensitiveCommands { get; set; } = false; } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index c24d35f6a..8920b25da 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -44,7 +44,12 @@ namespace Discord.Commands // both command and module provide aliases if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - Aliases = module.Aliases.Permutate(builder.Aliases, (first, second) => second != null ? first + " " + second : first).Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); + { + Aliases = module.Aliases + .Permutate(builder.Aliases, (first, second) => second != null ? first + service._separatorChar + second : first) + .Select(x => service._caseSensitive ? x : x.ToLowerInvariant()) + .ToImmutableArray(); + } // only module provides aliases else if (module.Aliases.Count > 0) Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); @@ -84,33 +89,19 @@ namespace Discord.Commands return PreconditionResult.FromSuccess(); } - - public async Task ParseAsync(CommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null) + + public async Task ParseAsync(CommandContext context, int startIndex, SearchResult searchResult, PreconditionResult? preconditionResult = null) { if (!searchResult.IsSuccess) return ParseResult.FromError(searchResult); if (preconditionResult != null && !preconditionResult.Value.IsSuccess) return ParseResult.FromError(preconditionResult.Value); - string input = searchResult.Text; - var matchingAliases = Aliases.Where(alias => input.StartsWith(alias)).ToArray(); - - string matchingAlias = null; - foreach (string alias in matchingAliases) - { - if (alias.Length > matchingAlias.Length) - matchingAlias = alias; - } - - if (matchingAlias == null) - return ParseResult.FromError(CommandError.ParseFailed, "Unable to find matching alias"); - - input = input.Substring(matchingAlias.Length); - + string input = searchResult.Text.Substring(startIndex); return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false); } - public Task Execute(CommandContext context, ParseResult parseResult, IDependencyMap map) + public Task ExecuteAsync(CommandContext context, ParseResult parseResult, IDependencyMap map) { if (!parseResult.IsSuccess) return Task.FromResult(ExecuteResult.FromError(parseResult)); diff --git a/src/Discord.Net.Commands/Map/CommandMap.cs b/src/Discord.Net.Commands/Map/CommandMap.cs index 3a5239878..bcff800d3 100644 --- a/src/Discord.Net.Commands/Map/CommandMap.cs +++ b/src/Discord.Net.Commands/Map/CommandMap.cs @@ -4,36 +4,30 @@ namespace Discord.Commands { internal class CommandMap { + private readonly CommandService _service; private readonly CommandMapNode _root; private static readonly string[] _blankAliases = new[] { "" }; - public CommandMap() + public CommandMap(CommandService service) { + _service = service; _root = new CommandMapNode(""); } public void AddCommand(CommandInfo command) { - foreach (string text in GetAliases(command)) - _root.AddCommand(text, 0, command); + foreach (string text in command.Aliases) + _root.AddCommand(_service, text, 0, command); } public void RemoveCommand(CommandInfo command) { - foreach (string text in GetAliases(command)) - _root.RemoveCommand(text, 0, command); + foreach (string text in command.Aliases) + _root.RemoveCommand(_service, text, 0, command); } - public IEnumerable GetCommands(string text) + public IEnumerable GetCommands(string text) { - return _root.GetCommands(text, 0); - } - - private IReadOnlyList GetAliases(CommandInfo command) - { - var aliases = command.Aliases; - if (aliases.Count == 0) - return _blankAliases; - return aliases; + return _root.GetCommands(_service, text, 0, text != ""); } } } diff --git a/src/Discord.Net.Commands/Map/CommandMapNode.cs b/src/Discord.Net.Commands/Map/CommandMapNode.cs index a86c0643d..863409207 100644 --- a/src/Discord.Net.Commands/Map/CommandMapNode.cs +++ b/src/Discord.Net.Commands/Map/CommandMapNode.cs @@ -7,7 +7,7 @@ namespace Discord.Commands { internal class CommandMapNode { - private static readonly char[] _whitespaceChars = new char[] { ' ', '\r', '\n' }; + private static readonly char[] _whitespaceChars = new[] { ' ', '\r', '\n' }; private readonly ConcurrentDictionary _nodes; private readonly string _name; @@ -23,9 +23,9 @@ namespace Discord.Commands _commands = ImmutableArray.Create(); } - public void AddCommand(string text, int index, CommandInfo command) + public void AddCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSegment = NextSegment(text, index, service._separatorChar); string name; lock (_lockObj) @@ -38,19 +38,20 @@ namespace Discord.Commands } else { - if (nextSpace == -1) + if (nextSegment == -1) name = text.Substring(index); else - name = text.Substring(index, nextSpace - index); + name = text.Substring(index, nextSegment - index); - var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x)); - nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + string fullName = _name == "" ? name : _name + service._separatorChar + name; + var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(fullName)); + nextNode.AddCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command); } } } - public void RemoveCommand(string text, int index, CommandInfo command) + public void RemoveCommand(CommandService service, string text, int index, CommandInfo command) { - int nextSpace = NextWhitespace(text, index); + int nextSegment = NextSegment(text, index, service._separatorChar); string name; lock (_lockObj) @@ -59,15 +60,15 @@ namespace Discord.Commands _commands = _commands.Remove(command); else { - if (nextSpace == -1) + if (nextSegment == -1) name = text.Substring(index); else - name = text.Substring(index, nextSpace - index); + name = text.Substring(index, nextSegment - index); CommandMapNode nextNode; if (_nodes.TryGetValue(name, out nextNode)) { - nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command); + nextNode.RemoveCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command); if (nextNode.IsEmpty) _nodes.TryRemove(name, out nextNode); } @@ -75,39 +76,58 @@ namespace Discord.Commands } } - public IEnumerable GetCommands(string text, int index) + public IEnumerable GetCommands(CommandService service, string text, int index, bool visitChildren = true) { - int nextSpace = NextWhitespace(text, index); - string name; - var commands = _commands; for (int i = 0; i < commands.Length; i++) - yield return _commands[i]; + yield return new CommandMatch(_commands[i], _name); - if (text != "") + if (visitChildren) { - if (nextSpace == -1) + string name; + CommandMapNode nextNode; + + //Search for next segment + int nextSegment = NextSegment(text, index, service._separatorChar); + if (nextSegment == -1) name = text.Substring(index); else - name = text.Substring(index, nextSpace - index); - - CommandMapNode nextNode; + name = text.Substring(index, nextSegment - index); if (_nodes.TryGetValue(name, out nextNode)) { - foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1)) + foreach (var cmd in nextNode.GetCommands(service, nextSegment == -1 ? "" : text, nextSegment + 1, true)) yield return cmd; } + + //Check if this is the last command segment before args + nextSegment = NextSegment(text, index, _whitespaceChars, service._separatorChar); + if (nextSegment != -1) + { + name = text.Substring(index, nextSegment - index); + if (_nodes.TryGetValue(name, out nextNode)) + { + foreach (var cmd in nextNode.GetCommands(service, nextSegment == -1 ? "" : text, nextSegment + 1, false)) + yield return cmd; + } + } } } - private static int NextWhitespace(string text, int startIndex) + private static int NextSegment(string text, int startIndex, char separator) + { + return text.IndexOf(separator, startIndex); + } + private static int NextSegment(string text, int startIndex, char[] separators, char except) { int lowest = int.MaxValue; - for (int i = 0; i < _whitespaceChars.Length; i++) + for (int i = 0; i < separators.Length; i++) { - int index = text.IndexOf(_whitespaceChars[i], startIndex); - if (index != -1 && index < lowest) - lowest = index; + if (separators[i] != except) + { + int index = text.IndexOf(separators[i], startIndex); + if (index != -1 && index < lowest) + lowest = index; + } } return (lowest != int.MaxValue) ? lowest : -1; } diff --git a/src/Discord.Net.Commands/Results/SearchResult.cs b/src/Discord.Net.Commands/Results/SearchResult.cs index 17942b61a..87d900d4d 100644 --- a/src/Discord.Net.Commands/Results/SearchResult.cs +++ b/src/Discord.Net.Commands/Results/SearchResult.cs @@ -7,14 +7,14 @@ namespace Discord.Commands public struct SearchResult : IResult { public string Text { get; } - public IReadOnlyList Commands { get; } + public IReadOnlyList Commands { get; } public CommandError? Error { get; } public string ErrorReason { get; } public bool IsSuccess => !Error.HasValue; - private SearchResult(string text, IReadOnlyList commands, CommandError? error, string errorReason) + private SearchResult(string text, IReadOnlyList commands, CommandError? error, string errorReason) { Text = text; Commands = commands; @@ -22,7 +22,7 @@ namespace Discord.Commands ErrorReason = errorReason; } - public static SearchResult FromSuccess(string text, IReadOnlyList commands) + public static SearchResult FromSuccess(string text, IReadOnlyList commands) => new SearchResult(text, commands, null, null); public static SearchResult FromError(CommandError error, string reason) => new SearchResult(null, null, error, reason); From c1a9bdc142bc216490b5c00cb87c8b973f4ebaeb Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 18 Dec 2016 18:14:39 -0400 Subject: [PATCH 099/263] Cleaned up alias permutations, fixed empty aliases --- .../Builders/CommandBuilder.cs | 6 +++- .../Builders/ModuleBuilder.cs | 8 +++-- src/Discord.Net.Commands/Info/CommandInfo.cs | 31 ++++++++----------- src/Discord.Net.Commands/Info/ModuleInfo.cs | 4 +-- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index a3f2fee78..3b14da263 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -72,7 +72,11 @@ namespace Discord.Commands.Builders public CommandBuilder AddAliases(params string[] aliases) { - _aliases.AddRange(aliases); + for (int i = 0; i < aliases.Length; i++) + { + if (!_aliases.Contains(aliases[i])) + _aliases.Add(aliases[i]); + } return this; } public CommandBuilder AddPrecondition(PreconditionAttribute precondition) diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 4a4b83497..1075825f8 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -58,9 +58,13 @@ namespace Discord.Commands.Builders return this; } - public ModuleBuilder AddAlias(params string[] newAliases) + public ModuleBuilder AddAlias(params string[] aliases) { - _aliases.AddRange(newAliases); + for (int i = 0; i < aliases.Length; i++) + { + if (!_aliases.Contains(aliases[i])) + _aliases.Add(aliases[i]); + } return this; } public ModuleBuilder AddPrecondition(PreconditionAttribute precondition) diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 8920b25da..abe802d53 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -41,24 +41,19 @@ namespace Discord.Commands RunMode = (builder.RunMode == RunMode.Default ? service._defaultRunMode : builder.RunMode); Priority = builder.Priority; - - // both command and module provide aliases - if (module.Aliases.Count > 0 && builder.Aliases.Count > 0) - { - Aliases = module.Aliases - .Permutate(builder.Aliases, (first, second) => second != null ? first + service._separatorChar + second : first) - .Select(x => service._caseSensitive ? x : x.ToLowerInvariant()) - .ToImmutableArray(); - } - // only module provides aliases - else if (module.Aliases.Count > 0) - Aliases = module.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); - // only command provides aliases - else if (builder.Aliases.Count > 0) - Aliases = builder.Aliases.Select(x => service._caseSensitive ? x : x.ToLowerInvariant()).ToImmutableArray(); - // neither provide aliases - else - throw new InvalidOperationException("Cannot build a command without any aliases"); + + Aliases = module.Aliases + .Permutate(builder.Aliases, (first, second) => + { + if (first == "") + return second; + else if (second == "") + return first; + else + return first + service._separatorChar + second; + }) + .Select(x => service._caseSensitive ? x : x.ToLowerInvariant()) + .ToImmutableArray(); Preconditions = builder.Preconditions.ToImmutableArray(); diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index ab4f65713..2a6439c02 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -15,7 +15,7 @@ namespace Discord.Commands public string Remarks { get; } public IReadOnlyList Aliases { get; } - public IEnumerable Commands { get; } + public IReadOnlyList Commands { get; } public IReadOnlyList Preconditions { get; } public IReadOnlyList Submodules { get; } public ModuleInfo Parent { get; } @@ -31,7 +31,7 @@ namespace Discord.Commands Parent = parent; Aliases = BuildAliases(builder).ToImmutableArray(); - Commands = builder.Commands.Select(x => x.Build(this, service)); + Commands = builder.Commands.Select(x => x.Build(this, service)).ToImmutableArray(); Preconditions = BuildPreconditions(builder).ToImmutableArray(); Submodules = BuildSubmodules(builder, service).ToImmutableArray(); From ae3ada84c499b7452530b678efe961be1ce237f7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 18 Dec 2016 18:21:02 -0400 Subject: [PATCH 100/263] Renamed ModuleBuilder.AddAlias -> AddAliases --- src/Discord.Net.Commands/Builders/ModuleBuilder.cs | 2 +- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 1075825f8..4e5b705f6 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -58,7 +58,7 @@ namespace Discord.Commands.Builders return this; } - public ModuleBuilder AddAlias(params string[] aliases) + public ModuleBuilder AddAliases(params string[] aliases) { for (int i = 0; i < aliases.Length; i++) { diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 776b799c9..7351c7e57 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -89,12 +89,12 @@ namespace Discord.Commands else if (attribute is RemarksAttribute) builder.Remarks = (attribute as RemarksAttribute).Text; else if (attribute is AliasAttribute) - builder.AddAlias((attribute as AliasAttribute).Aliases); + builder.AddAliases((attribute as AliasAttribute).Aliases); else if (attribute is GroupAttribute) { var groupAttr = attribute as GroupAttribute; builder.Name = builder.Name ?? groupAttr.Prefix; - builder.AddAlias(groupAttr.Prefix); + builder.AddAliases(groupAttr.Prefix); } else if (attribute is PreconditionAttribute) builder.AddPrecondition(attribute as PreconditionAttribute); From 419bc1e98a9a07711896192a7f67010165896446 Mon Sep 17 00:00:00 2001 From: Yivlx Date: Mon, 19 Dec 2016 14:00:29 -0800 Subject: [PATCH 101/263] Changed "his" to "this" Typically, you will want to mark his method as `async`, although it is not required. Changed `his` to `this` --- docs/guides/commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 8c33527ea..a0c6f83c0 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -68,7 +68,7 @@ By now, your module should look like this: The next step to creating commands, is actually creating commands. To create a command, add a method to your module of type `Task`. -Typically, you will want to mark his method as `async`, although it is +Typically, you will want to mark this method as `async`, although it is not required. Adding parameters to a command is done by adding parameters to the @@ -297,4 +297,4 @@ Otherwise, return `TypeReaderResult.FromError`. ### Installing TypeReaders TypeReaders are not automatically discovered by the Command Service, -and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). \ No newline at end of file +and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). From d27970113e7f4b2e1331f2636241192522bbe51f Mon Sep 17 00:00:00 2001 From: Confruggy Date: Mon, 19 Dec 2016 23:20:42 +0100 Subject: [PATCH 102/263] Update UserTypeReader.cs --- src/Discord.Net.Commands/Readers/UserTypeReader.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs index 31bdd0b58..82e8e9828 100644 --- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs @@ -46,13 +46,13 @@ namespace Discord.Commands ushort discriminator; if (ushort.TryParse(input.Substring(index + 1), out discriminator)) { - var channelUser = channelUsers.Where(x => x.DiscriminatorValue == discriminator && - string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - AddResult(results, channelUser as T, channelUser.Username == username ? 0.85f : 0.75f); + var channelUser = channelUsers.FirstOrDefault(x => x.DiscriminatorValue == discriminator && + string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase)); + AddResult(results, channelUser as T, channelUser?.Username == username ? 0.85f : 0.75f); - var guildUser = channelUsers.Where(x => x.DiscriminatorValue == discriminator && - string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); - AddResult(results, guildUser as T, guildUser.Username == username ? 0.80f : 0.70f); + var guildUser = channelUsers.FirstOrDefault(x => x.DiscriminatorValue == discriminator && + string.Equals(username, x.Username, StringComparison.OrdinalIgnoreCase)); + AddResult(results, guildUser as T, guildUser?.Username == username ? 0.80f : 0.70f); } } From bc985ada926e090941da0a7526eb15578d4bb8e4 Mon Sep 17 00:00:00 2001 From: AntiTcb Date: Mon, 19 Dec 2016 17:53:54 -0500 Subject: [PATCH 103/263] Modules without aliases now default to an empty string alias --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 2a6439c02..bff9d9a56 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -65,8 +65,8 @@ namespace Discord.Commands result = level.Aliases.Permutate(result, (second, first) => first + " " + second); } - if (result == null) //there were no aliases; default to an empty list - result = new List(); + if (result == null) //there were no aliases; default to an empty string alias + result = new List { "" }; return result; } From 5118ed876b60d1837ae2d990fc007e7f21a1cc60 Mon Sep 17 00:00:00 2001 From: AntiTcb Date: Mon, 19 Dec 2016 17:54:06 -0500 Subject: [PATCH 104/263] Can target classes now. --- src/Discord.Net.Commands/Attributes/AliasAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Attributes/AliasAttribute.cs b/src/Discord.Net.Commands/Attributes/AliasAttribute.cs index 9aa1371f6..6e115bd60 100644 --- a/src/Discord.Net.Commands/Attributes/AliasAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/AliasAttribute.cs @@ -3,7 +3,7 @@ using System; namespace Discord.Commands { /// Provides aliases for a command. - [AttributeUsage(AttributeTargets.Method)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AliasAttribute : Attribute { /// The aliases which have been defined for the command. From 1fd9452e6c4e44612b05082524f7304babbb2766 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 20 Dec 2016 09:41:42 -0400 Subject: [PATCH 105/263] Added WS4Net provider to build script --- build.bat | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.bat b/build.bat index 95a7c5d9a..203dfadd6 100644 --- a/build.bat +++ b/build.bat @@ -6,10 +6,12 @@ dotnet pack "src\Discord.Net.Commands" -c "%Configuration%" -o "artifacts" --ver dotnet pack "src\Discord.Net.Rest" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.WebSocket" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Rpc" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +dotnet pack "src\Discord.Net.Providers.WS4Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" \ No newline at end of file +REM dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +REM dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" \ No newline at end of file From 470f7fba7353b929dded1a3689ed3bea482a9c53 Mon Sep 17 00:00:00 2001 From: BoGuu Date: Wed, 21 Dec 2016 01:50:42 +0000 Subject: [PATCH 106/263] DefaultRetryMode never assigned - DRestAPIClient --- src/Discord.Net.Core/API/DiscordRestApiClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Core/API/DiscordRestApiClient.cs index a88e8bd0c..bd27ed3be 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Core/API/DiscordRestApiClient.cs @@ -52,6 +52,7 @@ namespace Discord.API { _restClientProvider = restClientProvider; UserAgent = userAgent; + DefaultRetryMode = defaultRetryMode; _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; RequestQueue = requestQueue; _fetchCurrentUser = fetchCurrentUser; From 7b1db319850b09f38b036947fb7fc34cdd640370 Mon Sep 17 00:00:00 2001 From: Confruggy Date: Wed, 21 Dec 2016 23:17:14 +0100 Subject: [PATCH 107/263] Update MessageHelper.cs --- src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 4f74129dd..e0777646e 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -36,7 +36,7 @@ namespace Discord.Rest } public static async Task RemoveReactionAsync(IMessage msg, IUser user, Emoji emoji, BaseDiscordClient client, RequestOptions options) - => await RemoveReactionAsync(msg, user, $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); + => await RemoveReactionAsync(msg, user, emoji.Id == 0 ? emoji.Name : $"{emoji.Name}:{emoji.Id}", client, options).ConfigureAwait(false); public static async Task RemoveReactionAsync(IMessage msg, IUser user, string emoji, BaseDiscordClient client, RequestOptions options) { From ca6eb6aff40d22b15b80ad53b54d25e8b955fc05 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 15:07:59 -0400 Subject: [PATCH 108/263] Added UDPClient Provider --- .../Discord.Net.Providers.UdpClient.csproj | 27 ++++ .../UDPClient.cs | 128 ++++++++++++++++++ .../UDPClientProvider.cs | 9 ++ .../project.json | 37 +++++ 4 files changed, 201 insertions(+) create mode 100644 src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj create mode 100644 src/Discord.Net.Providers.UDPClient/UDPClient.cs create mode 100644 src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs create mode 100644 src/Discord.Net.Providers.UDPClient/project.json diff --git a/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj new file mode 100644 index 000000000..64225a50d --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj @@ -0,0 +1,27 @@ + + + An optional UDP client provider for Discord.Net using System.Net.UdpClient + 1.0.0-beta2 + net45 + true + Discord.Net.Providers.UDPClient + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + Discord.Providers.UDPClient + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.Providers.UDPClient/UDPClient.cs b/src/Discord.Net.Providers.UDPClient/UDPClient.cs new file mode 100644 index 000000000..459feb335 --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/UDPClient.cs @@ -0,0 +1,128 @@ +using Discord.Net.Udp; +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using UdpSocket = System.Net.Sockets.UdpClient; + +namespace Discord.Net.Providers.UDPClient +{ + internal class UDPClient : IUdpSocket, IDisposable + { + public event Func ReceivedDatagram; + + private readonly SemaphoreSlim _lock; + private UdpSocket _udp; + private IPEndPoint _destination; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private Task _task; + private bool _isDisposed; + + public UDPClient() + { + _lock = new SemaphoreSlim(1, 1); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + StopInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + + public async Task StartAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StartInternalAsync(_cancelToken).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StartInternalAsync(CancellationToken cancelToken) + { + await StopInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _udp = new UdpSocket(); + + _task = RunAsync(_cancelToken); + } + public async Task StopAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StopInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StopInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_udp != null) + { + try { _udp.Close(); } + catch { } + _udp = null; + } + } + + public void SetDestination(string host, int port) + { + var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult(); + _destination = new IPEndPoint(entry.AddressList[0], port); + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count) + { + if (index != 0) //Should never happen? + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + await _udp.SendAsync(data, count, _destination).ConfigureAwait(false); + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var closeTask = Task.Delay(-1, cancelToken); + while (!cancelToken.IsCancellationRequested) + { + var receiveTask = _udp.ReceiveAsync(); + var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); + if (task == closeTask) + break; + + var result = receiveTask.Result; + await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs b/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs new file mode 100644 index 000000000..6bdf9eb63 --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs @@ -0,0 +1,9 @@ +using Discord.Net.Udp; + +namespace Discord.Net.Providers.UDPClient +{ + public static class UDPClientProvider + { + public static readonly UdpSocketProvider Instance = () => new UDPClient(); + } +} diff --git a/src/Discord.Net.Providers.UDPClient/project.json b/src/Discord.Net.Providers.UDPClient/project.json new file mode 100644 index 000000000..59d8509ad --- /dev/null +++ b/src/Discord.Net.Providers.UDPClient/project.json @@ -0,0 +1,37 @@ +{ + "version": "1.0.0-*", + "description": "An optional UDP client provider for Discord.Net using System.Net.UdpClient.", + "authors": [ "RogueException" ], + + "packOptions": { + "tags": [ "discord", "discordapp" ], + "licenseUrl": "http://opensource.org/licenses/MIT", + "projectUrl": "https://github.com/RogueException/Discord.Net", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + } + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Discord.Net.Core": { + "target": "project" + } + }, + + "frameworks": { + "net45": {} + } +} \ No newline at end of file From 8326d01f62bcbad7cb19f38a34c692180baedc5f Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 15:10:45 -0400 Subject: [PATCH 109/263] Isolated API definitions to their own library --- Discord.Net.sln | 37 +++++++++++++++--- build.bat | 6 ++- src/Discord.Net.API/AssemblyInfo.cs | 8 ++++ .../Common/Application.cs | 0 .../Common/Attachment.cs | 0 .../API => Discord.Net.API}/Common/Ban.cs | 0 .../API => Discord.Net.API}/Common/Channel.cs | 0 .../Common}/ChannelType.cs | 0 .../Common/Connection.cs | 0 .../Common}/DefaultMessageNotifications.cs | 0 .../Common}/Direction.cs | 0 .../API => Discord.Net.API}/Common/Embed.cs | 0 .../Common/EmbedAuthor.cs | 0 .../Common/EmbedField.cs | 0 .../Common/EmbedFooter.cs | 0 .../Common/EmbedImage.cs | 0 .../Common/EmbedProvider.cs | 0 .../Common/EmbedThumbnail.cs | 0 .../Common/EmbedVideo.cs | 0 .../API => Discord.Net.API}/Common/Emoji.cs | 0 .../API => Discord.Net.API}/Common/Game.cs | 0 .../API => Discord.Net.API}/Common/Guild.cs | 0 .../Common/GuildEmbed.cs | 0 .../Common/GuildMember.cs | 0 .../Common/Integration.cs | 0 .../Common/IntegrationAccount.cs | 0 .../API => Discord.Net.API}/Common/Invite.cs | 0 .../Common/InviteChannel.cs | 0 .../Common/InviteGuild.cs | 0 .../Common/InviteMetadata.cs | 0 .../API => Discord.Net.API}/Common/Message.cs | 0 .../Common}/MessageType.cs | 0 .../Common}/MfaLevel.cs | 0 .../Common/Overwrite.cs | 0 .../Common}/PermissionTarget.cs | 0 .../Common/Presence.cs | 0 .../Common/Reaction.cs | 0 .../Common/ReadState.cs | 0 .../Common/Relationship.cs | 0 .../Common/RelationshipType.cs | 0 .../API => Discord.Net.API}/Common/Role.cs | 0 .../Common}/StreamType.cs | 0 .../API => Discord.Net.API}/Common/User.cs | 0 .../Common/UserGuild.cs | 0 .../Common}/UserStatus.cs | 0 .../Common}/VerificationLevel.cs | 0 .../Common/VoiceRegion.cs | 0 .../Common/VoiceState.cs | 0 src/Discord.Net.API/Discord.Net.API.csproj | 29 ++++++++++++++ .../API => Discord.Net.API}/EntityOrId.cs | 0 .../Gateway/ExtendedGuild.cs | 0 .../Gateway/GatewayOpCode.cs | 0 .../Gateway/GuildBanEvent.cs | 0 .../Gateway/GuildEmojiUpdateEvent.cs | 0 .../Gateway/GuildMemberAddEvent.cs | 0 .../Gateway/GuildMemberRemoveEvent.cs | 0 .../Gateway/GuildMemberUpdateEvent.cs | 0 .../Gateway/GuildMembersChunkEvent.cs | 0 .../Gateway/GuildRoleCreateEvent.cs | 0 .../Gateway/GuildRoleDeleteEvent.cs | 0 .../Gateway/GuildRoleUpdateEvent.cs | 0 .../Gateway/GuildSyncEvent.cs | 0 .../Gateway/HelloEvent.cs | 0 .../Gateway/IdentifyParams.cs | 0 .../Gateway/MessageDeleteBulkEvent.cs | 0 .../Gateway/Reaction.cs | 0 .../Gateway/ReadyEvent.cs | 0 .../Gateway/RecipientEvent.cs | 0 .../Gateway/RemoveAllReactionsEvent.cs | 0 .../Gateway/RequestMembersParams.cs | 0 .../Gateway/ResumeParams.cs | 0 .../Gateway/ResumedEvent.cs | 0 .../Gateway/StatusUpdateParams.cs | 0 .../Gateway/TypingStartEvent.cs | 0 .../Gateway/VoiceServerUpdateEvent.cs | 0 .../Gateway/VoiceStateUpdateParams.cs | 3 -- .../API => Discord.Net.API}/Image.cs | 12 ------ .../API => Discord.Net.API}/Int53Attribute.cs | 0 .../Net}/MultipartFile.cs | 0 .../Utils => Discord.Net.API}/Optional.cs | 0 .../Rest/CreateChannelInviteParams.cs | 0 .../Rest/CreateDMChannelParams.cs | 0 .../Rest/CreateGuildBanParams.cs | 0 .../Rest/CreateGuildChannelParams.cs | 0 .../Rest/CreateGuildIntegrationParams.cs | 0 .../Rest/CreateGuildParams.cs | 0 .../Rest/CreateMessageParams.cs | 0 .../Rest/DeleteMessagesParams.cs | 0 .../Rest/GetChannelMessagesParams.cs | 0 .../Rest/GetGatewayResponse.cs | 0 .../Rest/GetGuildMembersParams.cs | 0 .../Rest/GetGuildPruneCountResponse.cs | 0 .../Rest/GetReactionUsersParams.cs | 0 .../Rest/GuildPruneParams.cs | 0 .../Rest/ModifyChannelPermissionsParams.cs | 0 .../Rest/ModifyCurrentUserNickParams.cs | 0 .../Rest/ModifyCurrentUserParams.cs | 0 .../Rest/ModifyGuildChannelParams.cs | 0 .../Rest/ModifyGuildChannelsParams.cs | 0 .../Rest/ModifyGuildEmbedParams.cs | 0 .../Rest/ModifyGuildIntegrationParams.cs | 0 .../Rest/ModifyGuildMemberParams.cs | 0 .../Rest/ModifyGuildParams.cs | 0 .../Rest/ModifyGuildRoleParams.cs | 0 .../Rest/ModifyGuildRolesParams.cs | 0 .../Rest/ModifyMessageParams.cs | 0 .../Rest/ModifyTextChannelParams.cs | 0 .../Rest/ModifyVoiceChannelParams.cs | 0 .../Rest/UploadFileParams.cs | 0 .../Rpc/AuthenticateParams.cs | 0 .../Rpc/AuthenticateResponse.cs | 0 .../Rpc/AuthorizeParams.cs | 0 .../Rpc/AuthorizeResponse.cs | 0 .../API => Discord.Net.API}/Rpc/Channel.cs | 0 .../Rpc/ChannelSubscriptionParams.cs | 0 .../Rpc/ChannelSummary.cs | 0 .../API => Discord.Net.API}/Rpc/ErrorEvent.cs | 0 .../Rpc/ExtendedVoiceState.cs | 0 .../Rpc/GetChannelParams.cs | 0 .../Rpc/GetChannelsParams.cs | 0 .../Rpc/GetChannelsResponse.cs | 0 .../Rpc/GetGuildParams.cs | 0 .../Rpc/GetGuildsParams.cs | 0 .../Rpc/GetGuildsResponse.cs | 0 .../API => Discord.Net.API}/Rpc/Guild.cs | 0 .../Rpc/GuildMember.cs | 0 .../Rpc/GuildStatusEvent.cs | 0 .../Rpc/GuildSubscriptionParams.cs | 0 .../Rpc/GuildSummary.cs | 0 .../API => Discord.Net.API}/Rpc/Message.cs | 0 .../Rpc/MessageEvent.cs | 0 .../API => Discord.Net.API}/Rpc/Pan.cs | 0 .../API => Discord.Net.API}/Rpc/ReadyEvent.cs | 0 .../API => Discord.Net.API}/Rpc/RpcConfig.cs | 0 .../Rpc/SelectChannelParams.cs | 0 .../Rpc/SetLocalVolumeParams.cs | 0 .../Rpc/SetLocalVolumeResponse.cs | 0 .../Rpc/SpeakingEvent.cs | 0 .../Rpc/SubscriptionResponse.cs | 0 .../Rpc/UserVoiceSettings.cs | 0 .../Rpc/VoiceDevice.cs | 0 .../Rpc/VoiceDeviceSettings.cs | 0 .../API => Discord.Net.API}/Rpc/VoiceMode.cs | 0 .../Rpc/VoiceSettings.cs | 0 .../Rpc/VoiceShortcut.cs | 0 .../Rpc}/VoiceShortcutType.cs | 0 .../API => Discord.Net.API}/RpcFrame.cs | 0 .../API => Discord.Net.API}/SocketFrame.cs | 0 .../Voice/IdentifyParams.cs | 0 .../Voice/ReadyEvent.cs | 0 .../Voice/SelectProtocolParams.cs | 0 .../Voice/SessionDescriptionEvent.cs | 0 .../Voice/SpeakingParams.cs | 0 .../Voice/UdpProtocolInfo.cs | 0 .../Voice/VoiceOpCode.cs | 0 src/Discord.Net.API/project.json | 38 +++++++++++++++++++ .../Discord.Net.Commands.csproj | 2 + src/Discord.Net.Core/Audio/IAudioClient.cs | 2 +- src/Discord.Net.Core/{API => }/CDN.cs | 2 +- src/Discord.Net.Core/Discord.Net.Core.csproj | 11 +++++- src/Discord.Net.Core/Entities/Image.cs | 6 +++ .../Entities/Users/ModifyCurrentUserParams.cs | 2 +- src/Discord.Net.Core/IDiscordClient.cs | 2 - src/Discord.Net.Core/project.json | 4 ++ .../Discord.Net.Providers.WS4Net.csproj | 1 + .../WS4NetClient.cs | 4 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 2 + .../DiscordRestApiClient.cs | 6 +-- src/Discord.Net.Rest/DiscordRestClient.cs | 2 +- .../Entities/Channels/ChannelHelper.cs | 8 ++-- .../Entities/Channels/RestTextChannel.cs | 2 +- .../Entities/Channels/RestVoiceChannel.cs | 2 +- .../Entities/Guilds/GuildHelper.cs | 6 +-- .../Entities/Guilds/RestGuild.cs | 6 +-- .../Entities/Guilds/RestUserGuild.cs | 2 +- .../Entities/RestApplication.cs | 2 +- .../Entities/Users/RestUser.cs | 2 +- .../Entities/Users/UserHelper.cs | 2 +- .../Net/Queue/ClientBucket.cs | 0 .../Net/Queue/RequestQueue.cs | 2 +- .../Net/Queue/RequestQueueBucket.cs | 0 .../Net/Queue/Requests/JsonRestRequest.cs | 0 .../Queue/Requests/MultipartRestRequest.cs | 0 .../Net/Queue/Requests/RestRequest.cs | 0 .../Net/Queue/Requests/WebSocketRequest.cs | 0 .../Net/RateLimitInfo.cs | 2 +- src/Discord.Net.Rest/Utils/TypingNotifier.cs | 2 +- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 5 +-- .../{API => }/DiscordRpcApiClient.cs | 7 ++-- src/Discord.Net.Rpc/DiscordRpcClient.cs | 2 +- src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 2 +- .../Audio/AudioClient.cs | 6 +-- .../Audio/Opus/OpusApplication.cs | 0 .../Audio/Streams/OpusEncodeStream.cs | 3 +- .../Discord.Net.WebSocket.csproj | 2 + .../{API => }/DiscordSocketApiClient.cs | 4 +- .../DiscordSocketClient.cs | 4 +- .../{API => }/DiscordVoiceApiClient.cs | 0 .../Entities/Users/SocketUser.cs | 2 +- src/Discord.Net/Discord.Net.csproj | 2 + 200 files changed, 183 insertions(+), 73 deletions(-) create mode 100644 src/Discord.Net.API/AssemblyInfo.cs rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Application.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Attachment.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Ban.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Channel.cs (100%) rename src/{Discord.Net.Core/Entities/Channels => Discord.Net.API/Common}/ChannelType.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Connection.cs (100%) rename src/{Discord.Net.Core/Entities/Guilds => Discord.Net.API/Common}/DefaultMessageNotifications.cs (100%) rename src/{Discord.Net.Core/Entities/Messages => Discord.Net.API/Common}/Direction.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Embed.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedAuthor.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedField.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedFooter.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedImage.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedProvider.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedThumbnail.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/EmbedVideo.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Emoji.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Game.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Guild.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/GuildEmbed.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/GuildMember.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Integration.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/IntegrationAccount.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Invite.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/InviteChannel.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/InviteGuild.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/InviteMetadata.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Message.cs (100%) rename src/{Discord.Net.Core/Entities/Messages => Discord.Net.API/Common}/MessageType.cs (100%) rename src/{Discord.Net.Core/Entities/Guilds => Discord.Net.API/Common}/MfaLevel.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Overwrite.cs (100%) rename src/{Discord.Net.Core/Entities/Permissions => Discord.Net.API/Common}/PermissionTarget.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Presence.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Reaction.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/ReadState.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Relationship.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/RelationshipType.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/Role.cs (100%) rename src/{Discord.Net.Core/Entities/Users => Discord.Net.API/Common}/StreamType.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/User.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/UserGuild.cs (100%) rename src/{Discord.Net.Core/Entities/Users => Discord.Net.API/Common}/UserStatus.cs (100%) rename src/{Discord.Net.Core/Entities/Guilds => Discord.Net.API/Common}/VerificationLevel.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/VoiceRegion.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Common/VoiceState.cs (100%) create mode 100644 src/Discord.Net.API/Discord.Net.API.csproj rename src/{Discord.Net.Core/API => Discord.Net.API}/EntityOrId.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/ExtendedGuild.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GatewayOpCode.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildBanEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildEmojiUpdateEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildMemberAddEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildMemberRemoveEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildMemberUpdateEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildMembersChunkEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildRoleCreateEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildRoleDeleteEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildRoleUpdateEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/GuildSyncEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/HelloEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/IdentifyParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/MessageDeleteBulkEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/Reaction.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/ReadyEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/RecipientEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/RemoveAllReactionsEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/RequestMembersParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/ResumeParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/ResumedEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/StatusUpdateParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/TypingStartEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/VoiceServerUpdateEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Gateway/VoiceStateUpdateParams.cs (80%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Image.cs (52%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Int53Attribute.cs (100%) rename src/{Discord.Net.Core/Net/Rest => Discord.Net.API/Net}/MultipartFile.cs (100%) rename src/{Discord.Net.Core/Utils => Discord.Net.API}/Optional.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateChannelInviteParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateDMChannelParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateGuildBanParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateGuildChannelParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateGuildIntegrationParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateGuildParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/CreateMessageParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/DeleteMessagesParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GetChannelMessagesParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GetGatewayResponse.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GetGuildMembersParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GetGuildPruneCountResponse.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GetReactionUsersParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/GuildPruneParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyChannelPermissionsParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyCurrentUserNickParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyCurrentUserParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildChannelParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildChannelsParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildEmbedParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildIntegrationParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildMemberParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildRoleParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyGuildRolesParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyMessageParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyTextChannelParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/ModifyVoiceChannelParams.cs (100%) rename src/{Discord.Net.Core/API => Discord.Net.API}/Rest/UploadFileParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/AuthenticateParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/AuthenticateResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/AuthorizeParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/AuthorizeResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/Channel.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/ChannelSubscriptionParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/ChannelSummary.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/ErrorEvent.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/ExtendedVoiceState.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetChannelParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetChannelsParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetChannelsResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetGuildParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetGuildsParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GetGuildsResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/Guild.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GuildMember.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GuildStatusEvent.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GuildSubscriptionParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/GuildSummary.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/Message.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/MessageEvent.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/Pan.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/ReadyEvent.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/RpcConfig.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/SelectChannelParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/SetLocalVolumeParams.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/SetLocalVolumeResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/SpeakingEvent.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/SubscriptionResponse.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/UserVoiceSettings.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/VoiceDevice.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/VoiceDeviceSettings.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/VoiceMode.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/VoiceSettings.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/Rpc/VoiceShortcut.cs (100%) rename src/{Discord.Net.Rpc/Entities => Discord.Net.API/Rpc}/VoiceShortcutType.cs (100%) rename src/{Discord.Net.Rpc/API => Discord.Net.API}/RpcFrame.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/SocketFrame.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/IdentifyParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/ReadyEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/SelectProtocolParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/SessionDescriptionEvent.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/SpeakingParams.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/UdpProtocolInfo.cs (100%) rename src/{Discord.Net.WebSocket/API => Discord.Net.API}/Voice/VoiceOpCode.cs (100%) create mode 100644 src/Discord.Net.API/project.json rename src/Discord.Net.Core/{API => }/CDN.cs (97%) rename src/{Discord.Net.Core/API => Discord.Net.Rest}/DiscordRestApiClient.cs (99%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/ClientBucket.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/RequestQueue.cs (99%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/RequestQueueBucket.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/Requests/JsonRestRequest.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/Requests/MultipartRestRequest.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/Requests/RestRequest.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Queue/Requests/WebSocketRequest.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/RateLimitInfo.cs (96%) rename src/Discord.Net.Rpc/{API => }/DiscordRpcApiClient.cs (99%) rename src/{Discord.Net.Core => Discord.Net.WebSocket}/Audio/Opus/OpusApplication.cs (100%) rename src/Discord.Net.WebSocket/{API => }/DiscordSocketApiClient.cs (99%) rename src/Discord.Net.WebSocket/{API => }/DiscordVoiceApiClient.cs (100%) diff --git a/Discord.Net.sln b/Discord.Net.sln index 9663b21dc..c0715c29b 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -3,11 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26014.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7F3E124-93C7-4846-AE87-9CE12BD82859}" - ProjectSection(SolutionItems) = preProject - README.md = README.md - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net", "src\Discord.Net\Discord.Net.csproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" @@ -26,6 +21,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.WS4Net", "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj", "{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.API", "src\Discord.Net.API\Discord.Net.API.csproj", "{547261FC-8BA3-40EA-A040-A38ABDAA8D72}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.UDPClient", "src\Discord.Net.Providers.UDPClient\Discord.Net.Providers.UDPClient.csproj", "{ABC9F4B9-2452-4725-B522-754E0A02E282}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -120,6 +121,30 @@ Global {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x64.Build.0 = Release|x64 {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x86.ActiveCfg = Release|x86 {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}.Release|x86.Build.0 = Release|x86 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|Any CPU.Build.0 = Debug|Any CPU + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|x64.ActiveCfg = Debug|x64 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|x64.Build.0 = Debug|x64 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|x86.ActiveCfg = Debug|x86 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Debug|x86.Build.0 = Debug|x86 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|Any CPU.ActiveCfg = Release|Any CPU + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|Any CPU.Build.0 = Release|Any CPU + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|x64.ActiveCfg = Release|x64 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|x64.Build.0 = Release|x64 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|x86.ActiveCfg = Release|x86 + {547261FC-8BA3-40EA-A040-A38ABDAA8D72}.Release|x86.Build.0 = Release|x86 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|x64.ActiveCfg = Debug|x64 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|x64.Build.0 = Debug|x64 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|x86.ActiveCfg = Debug|x86 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Debug|x86.Build.0 = Debug|x86 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|Any CPU.Build.0 = Release|Any CPU + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x64.ActiveCfg = Release|x64 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x64.Build.0 = Release|x64 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x86.ActiveCfg = Release|x86 + {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -127,7 +152,9 @@ Global GlobalSection(NestedProjects) = preSolution {BFC6DC28-0351-4573-926A-D4124244C04F} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {5688A353-121E-40A1-8BFA-B17B91FB48FB} = {288C363D-A636-4EAE-9AC1-4698B641B26E} + {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} + {ABC9F4B9-2452-4725-B522-754E0A02E282} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} EndGlobalSection EndGlobal diff --git a/build.bat b/build.bat index 203dfadd6..1de188f4e 100644 --- a/build.bat +++ b/build.bat @@ -1,17 +1,21 @@ @echo Off dotnet restore dotnet pack "src\Discord.Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +dotnet pack "src\Discord.Net.API" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Core" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Commands" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Rest" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.WebSocket" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Rpc" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" dotnet pack "src\Discord.Net.Providers.WS4Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +dotnet pack "src\Discord.Net.Providers.UDPClient" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +REM dotnet pack "src\Discord.Net.API\Discord.Net.API.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" REM dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" \ No newline at end of file +REM dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" +REM dotnet pack "src\Discord.Net.Providers.UDPClient\Discord.Net.Providers.UDPClient.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" \ No newline at end of file diff --git a/src/Discord.Net.API/AssemblyInfo.cs b/src/Discord.Net.API/AssemblyInfo.cs new file mode 100644 index 000000000..49872adaf --- /dev/null +++ b/src/Discord.Net.API/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Discord.Net.Core")] +[assembly: InternalsVisibleTo("Discord.Net.Rest")] +[assembly: InternalsVisibleTo("Discord.Net.Rpc")] +[assembly: InternalsVisibleTo("Discord.Net.WebSocket")] +[assembly: InternalsVisibleTo("Discord.Net.Commands")] +[assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file diff --git a/src/Discord.Net.Core/API/Common/Application.cs b/src/Discord.Net.API/Common/Application.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Application.cs rename to src/Discord.Net.API/Common/Application.cs diff --git a/src/Discord.Net.Core/API/Common/Attachment.cs b/src/Discord.Net.API/Common/Attachment.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Attachment.cs rename to src/Discord.Net.API/Common/Attachment.cs diff --git a/src/Discord.Net.Core/API/Common/Ban.cs b/src/Discord.Net.API/Common/Ban.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Ban.cs rename to src/Discord.Net.API/Common/Ban.cs diff --git a/src/Discord.Net.Core/API/Common/Channel.cs b/src/Discord.Net.API/Common/Channel.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Channel.cs rename to src/Discord.Net.API/Common/Channel.cs diff --git a/src/Discord.Net.Core/Entities/Channels/ChannelType.cs b/src/Discord.Net.API/Common/ChannelType.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Channels/ChannelType.cs rename to src/Discord.Net.API/Common/ChannelType.cs diff --git a/src/Discord.Net.Core/API/Common/Connection.cs b/src/Discord.Net.API/Common/Connection.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Connection.cs rename to src/Discord.Net.API/Common/Connection.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs b/src/Discord.Net.API/Common/DefaultMessageNotifications.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs rename to src/Discord.Net.API/Common/DefaultMessageNotifications.cs diff --git a/src/Discord.Net.Core/Entities/Messages/Direction.cs b/src/Discord.Net.API/Common/Direction.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Messages/Direction.cs rename to src/Discord.Net.API/Common/Direction.cs diff --git a/src/Discord.Net.Core/API/Common/Embed.cs b/src/Discord.Net.API/Common/Embed.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Embed.cs rename to src/Discord.Net.API/Common/Embed.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedAuthor.cs b/src/Discord.Net.API/Common/EmbedAuthor.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedAuthor.cs rename to src/Discord.Net.API/Common/EmbedAuthor.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedField.cs b/src/Discord.Net.API/Common/EmbedField.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedField.cs rename to src/Discord.Net.API/Common/EmbedField.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedFooter.cs b/src/Discord.Net.API/Common/EmbedFooter.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedFooter.cs rename to src/Discord.Net.API/Common/EmbedFooter.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedImage.cs b/src/Discord.Net.API/Common/EmbedImage.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedImage.cs rename to src/Discord.Net.API/Common/EmbedImage.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedProvider.cs b/src/Discord.Net.API/Common/EmbedProvider.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedProvider.cs rename to src/Discord.Net.API/Common/EmbedProvider.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedThumbnail.cs b/src/Discord.Net.API/Common/EmbedThumbnail.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedThumbnail.cs rename to src/Discord.Net.API/Common/EmbedThumbnail.cs diff --git a/src/Discord.Net.Core/API/Common/EmbedVideo.cs b/src/Discord.Net.API/Common/EmbedVideo.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/EmbedVideo.cs rename to src/Discord.Net.API/Common/EmbedVideo.cs diff --git a/src/Discord.Net.Core/API/Common/Emoji.cs b/src/Discord.Net.API/Common/Emoji.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Emoji.cs rename to src/Discord.Net.API/Common/Emoji.cs diff --git a/src/Discord.Net.Core/API/Common/Game.cs b/src/Discord.Net.API/Common/Game.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Game.cs rename to src/Discord.Net.API/Common/Game.cs diff --git a/src/Discord.Net.Core/API/Common/Guild.cs b/src/Discord.Net.API/Common/Guild.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Guild.cs rename to src/Discord.Net.API/Common/Guild.cs diff --git a/src/Discord.Net.Core/API/Common/GuildEmbed.cs b/src/Discord.Net.API/Common/GuildEmbed.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/GuildEmbed.cs rename to src/Discord.Net.API/Common/GuildEmbed.cs diff --git a/src/Discord.Net.Core/API/Common/GuildMember.cs b/src/Discord.Net.API/Common/GuildMember.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/GuildMember.cs rename to src/Discord.Net.API/Common/GuildMember.cs diff --git a/src/Discord.Net.Core/API/Common/Integration.cs b/src/Discord.Net.API/Common/Integration.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Integration.cs rename to src/Discord.Net.API/Common/Integration.cs diff --git a/src/Discord.Net.Core/API/Common/IntegrationAccount.cs b/src/Discord.Net.API/Common/IntegrationAccount.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/IntegrationAccount.cs rename to src/Discord.Net.API/Common/IntegrationAccount.cs diff --git a/src/Discord.Net.Core/API/Common/Invite.cs b/src/Discord.Net.API/Common/Invite.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Invite.cs rename to src/Discord.Net.API/Common/Invite.cs diff --git a/src/Discord.Net.Core/API/Common/InviteChannel.cs b/src/Discord.Net.API/Common/InviteChannel.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/InviteChannel.cs rename to src/Discord.Net.API/Common/InviteChannel.cs diff --git a/src/Discord.Net.Core/API/Common/InviteGuild.cs b/src/Discord.Net.API/Common/InviteGuild.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/InviteGuild.cs rename to src/Discord.Net.API/Common/InviteGuild.cs diff --git a/src/Discord.Net.Core/API/Common/InviteMetadata.cs b/src/Discord.Net.API/Common/InviteMetadata.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/InviteMetadata.cs rename to src/Discord.Net.API/Common/InviteMetadata.cs diff --git a/src/Discord.Net.Core/API/Common/Message.cs b/src/Discord.Net.API/Common/Message.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Message.cs rename to src/Discord.Net.API/Common/Message.cs diff --git a/src/Discord.Net.Core/Entities/Messages/MessageType.cs b/src/Discord.Net.API/Common/MessageType.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Messages/MessageType.cs rename to src/Discord.Net.API/Common/MessageType.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs b/src/Discord.Net.API/Common/MfaLevel.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs rename to src/Discord.Net.API/Common/MfaLevel.cs diff --git a/src/Discord.Net.Core/API/Common/Overwrite.cs b/src/Discord.Net.API/Common/Overwrite.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Overwrite.cs rename to src/Discord.Net.API/Common/Overwrite.cs diff --git a/src/Discord.Net.Core/Entities/Permissions/PermissionTarget.cs b/src/Discord.Net.API/Common/PermissionTarget.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Permissions/PermissionTarget.cs rename to src/Discord.Net.API/Common/PermissionTarget.cs diff --git a/src/Discord.Net.Core/API/Common/Presence.cs b/src/Discord.Net.API/Common/Presence.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Presence.cs rename to src/Discord.Net.API/Common/Presence.cs diff --git a/src/Discord.Net.Core/API/Common/Reaction.cs b/src/Discord.Net.API/Common/Reaction.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Reaction.cs rename to src/Discord.Net.API/Common/Reaction.cs diff --git a/src/Discord.Net.Core/API/Common/ReadState.cs b/src/Discord.Net.API/Common/ReadState.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/ReadState.cs rename to src/Discord.Net.API/Common/ReadState.cs diff --git a/src/Discord.Net.Core/API/Common/Relationship.cs b/src/Discord.Net.API/Common/Relationship.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Relationship.cs rename to src/Discord.Net.API/Common/Relationship.cs diff --git a/src/Discord.Net.Core/API/Common/RelationshipType.cs b/src/Discord.Net.API/Common/RelationshipType.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/RelationshipType.cs rename to src/Discord.Net.API/Common/RelationshipType.cs diff --git a/src/Discord.Net.Core/API/Common/Role.cs b/src/Discord.Net.API/Common/Role.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/Role.cs rename to src/Discord.Net.API/Common/Role.cs diff --git a/src/Discord.Net.Core/Entities/Users/StreamType.cs b/src/Discord.Net.API/Common/StreamType.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Users/StreamType.cs rename to src/Discord.Net.API/Common/StreamType.cs diff --git a/src/Discord.Net.Core/API/Common/User.cs b/src/Discord.Net.API/Common/User.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/User.cs rename to src/Discord.Net.API/Common/User.cs diff --git a/src/Discord.Net.Core/API/Common/UserGuild.cs b/src/Discord.Net.API/Common/UserGuild.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/UserGuild.cs rename to src/Discord.Net.API/Common/UserGuild.cs diff --git a/src/Discord.Net.Core/Entities/Users/UserStatus.cs b/src/Discord.Net.API/Common/UserStatus.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Users/UserStatus.cs rename to src/Discord.Net.API/Common/UserStatus.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs b/src/Discord.Net.API/Common/VerificationLevel.cs similarity index 100% rename from src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs rename to src/Discord.Net.API/Common/VerificationLevel.cs diff --git a/src/Discord.Net.Core/API/Common/VoiceRegion.cs b/src/Discord.Net.API/Common/VoiceRegion.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/VoiceRegion.cs rename to src/Discord.Net.API/Common/VoiceRegion.cs diff --git a/src/Discord.Net.Core/API/Common/VoiceState.cs b/src/Discord.Net.API/Common/VoiceState.cs similarity index 100% rename from src/Discord.Net.Core/API/Common/VoiceState.cs rename to src/Discord.Net.API/Common/VoiceState.cs diff --git a/src/Discord.Net.API/Discord.Net.API.csproj b/src/Discord.Net.API/Discord.Net.API.csproj new file mode 100644 index 000000000..aa4462e9e --- /dev/null +++ b/src/Discord.Net.API/Discord.Net.API.csproj @@ -0,0 +1,29 @@ + + + A collection of Discord API definitions for use by Discord.Net. + 1.0.0-beta2 + netstandard1.1 + Discord.Net.API + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net.API + Discord + + + + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.Core/API/EntityOrId.cs b/src/Discord.Net.API/EntityOrId.cs similarity index 100% rename from src/Discord.Net.Core/API/EntityOrId.cs rename to src/Discord.Net.API/EntityOrId.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs b/src/Discord.Net.API/Gateway/ExtendedGuild.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs rename to src/Discord.Net.API/Gateway/ExtendedGuild.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs b/src/Discord.Net.API/Gateway/GatewayOpCode.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs rename to src/Discord.Net.API/Gateway/GatewayOpCode.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs b/src/Discord.Net.API/Gateway/GuildBanEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs rename to src/Discord.Net.API/Gateway/GuildBanEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs b/src/Discord.Net.API/Gateway/GuildEmojiUpdateEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs rename to src/Discord.Net.API/Gateway/GuildEmojiUpdateEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs b/src/Discord.Net.API/Gateway/GuildMemberAddEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs rename to src/Discord.Net.API/Gateway/GuildMemberAddEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs b/src/Discord.Net.API/Gateway/GuildMemberRemoveEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs rename to src/Discord.Net.API/Gateway/GuildMemberRemoveEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs b/src/Discord.Net.API/Gateway/GuildMemberUpdateEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs rename to src/Discord.Net.API/Gateway/GuildMemberUpdateEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs b/src/Discord.Net.API/Gateway/GuildMembersChunkEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs rename to src/Discord.Net.API/Gateway/GuildMembersChunkEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs b/src/Discord.Net.API/Gateway/GuildRoleCreateEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs rename to src/Discord.Net.API/Gateway/GuildRoleCreateEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs b/src/Discord.Net.API/Gateway/GuildRoleDeleteEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs rename to src/Discord.Net.API/Gateway/GuildRoleDeleteEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs b/src/Discord.Net.API/Gateway/GuildRoleUpdateEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs rename to src/Discord.Net.API/Gateway/GuildRoleUpdateEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs b/src/Discord.Net.API/Gateway/GuildSyncEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs rename to src/Discord.Net.API/Gateway/GuildSyncEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs b/src/Discord.Net.API/Gateway/HelloEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs rename to src/Discord.Net.API/Gateway/HelloEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs b/src/Discord.Net.API/Gateway/IdentifyParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs rename to src/Discord.Net.API/Gateway/IdentifyParams.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs b/src/Discord.Net.API/Gateway/MessageDeleteBulkEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs rename to src/Discord.Net.API/Gateway/MessageDeleteBulkEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs b/src/Discord.Net.API/Gateway/Reaction.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/Reaction.cs rename to src/Discord.Net.API/Gateway/Reaction.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs b/src/Discord.Net.API/Gateway/ReadyEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs rename to src/Discord.Net.API/Gateway/ReadyEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs b/src/Discord.Net.API/Gateway/RecipientEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs rename to src/Discord.Net.API/Gateway/RecipientEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs b/src/Discord.Net.API/Gateway/RemoveAllReactionsEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs rename to src/Discord.Net.API/Gateway/RemoveAllReactionsEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs b/src/Discord.Net.API/Gateway/RequestMembersParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs rename to src/Discord.Net.API/Gateway/RequestMembersParams.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs b/src/Discord.Net.API/Gateway/ResumeParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs rename to src/Discord.Net.API/Gateway/ResumeParams.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs b/src/Discord.Net.API/Gateway/ResumedEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs rename to src/Discord.Net.API/Gateway/ResumedEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs b/src/Discord.Net.API/Gateway/StatusUpdateParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs rename to src/Discord.Net.API/Gateway/StatusUpdateParams.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs b/src/Discord.Net.API/Gateway/TypingStartEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs rename to src/Discord.Net.API/Gateway/TypingStartEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs b/src/Discord.Net.API/Gateway/VoiceServerUpdateEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs rename to src/Discord.Net.API/Gateway/VoiceServerUpdateEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs b/src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs similarity index 80% rename from src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs rename to src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs index e75e4412c..f08973cb5 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs +++ b/src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs @@ -13,10 +13,7 @@ namespace Discord.API.Gateway [JsonProperty("guild_id")] public ulong? GuildId { get; set; } - public IGuild Guild { set { GuildId = value?.Id; } } - [JsonProperty("channel_id")] public ulong? ChannelId { get; set; } - public IChannel Channel { set { ChannelId = value?.Id; } } } } diff --git a/src/Discord.Net.Core/API/Image.cs b/src/Discord.Net.API/Image.cs similarity index 52% rename from src/Discord.Net.Core/API/Image.cs rename to src/Discord.Net.API/Image.cs index a500ebfd5..5442bd30f 100644 --- a/src/Discord.Net.Core/API/Image.cs +++ b/src/Discord.Net.API/Image.cs @@ -17,17 +17,5 @@ namespace Discord.API Stream = null; Hash = hash; } - - internal static Image Create(Discord.Image image) - { - return new Image(image.Stream); - } - internal static Image? Create(Discord.Image? image) - { - if (image.HasValue) - return new Image(image.Value.Stream); - else - return null; - } } } diff --git a/src/Discord.Net.Core/API/Int53Attribute.cs b/src/Discord.Net.API/Int53Attribute.cs similarity index 100% rename from src/Discord.Net.Core/API/Int53Attribute.cs rename to src/Discord.Net.API/Int53Attribute.cs diff --git a/src/Discord.Net.Core/Net/Rest/MultipartFile.cs b/src/Discord.Net.API/Net/MultipartFile.cs similarity index 100% rename from src/Discord.Net.Core/Net/Rest/MultipartFile.cs rename to src/Discord.Net.API/Net/MultipartFile.cs diff --git a/src/Discord.Net.Core/Utils/Optional.cs b/src/Discord.Net.API/Optional.cs similarity index 100% rename from src/Discord.Net.Core/Utils/Optional.cs rename to src/Discord.Net.API/Optional.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateChannelInviteParams.cs b/src/Discord.Net.API/Rest/CreateChannelInviteParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateChannelInviteParams.cs rename to src/Discord.Net.API/Rest/CreateChannelInviteParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateDMChannelParams.cs b/src/Discord.Net.API/Rest/CreateDMChannelParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateDMChannelParams.cs rename to src/Discord.Net.API/Rest/CreateDMChannelParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateGuildBanParams.cs b/src/Discord.Net.API/Rest/CreateGuildBanParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateGuildBanParams.cs rename to src/Discord.Net.API/Rest/CreateGuildBanParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net.API/Rest/CreateGuildChannelParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateGuildChannelParams.cs rename to src/Discord.Net.API/Rest/CreateGuildChannelParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateGuildIntegrationParams.cs b/src/Discord.Net.API/Rest/CreateGuildIntegrationParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateGuildIntegrationParams.cs rename to src/Discord.Net.API/Rest/CreateGuildIntegrationParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateGuildParams.cs b/src/Discord.Net.API/Rest/CreateGuildParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateGuildParams.cs rename to src/Discord.Net.API/Rest/CreateGuildParams.cs diff --git a/src/Discord.Net.Core/API/Rest/CreateMessageParams.cs b/src/Discord.Net.API/Rest/CreateMessageParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/CreateMessageParams.cs rename to src/Discord.Net.API/Rest/CreateMessageParams.cs diff --git a/src/Discord.Net.Core/API/Rest/DeleteMessagesParams.cs b/src/Discord.Net.API/Rest/DeleteMessagesParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/DeleteMessagesParams.cs rename to src/Discord.Net.API/Rest/DeleteMessagesParams.cs diff --git a/src/Discord.Net.Core/API/Rest/GetChannelMessagesParams.cs b/src/Discord.Net.API/Rest/GetChannelMessagesParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GetChannelMessagesParams.cs rename to src/Discord.Net.API/Rest/GetChannelMessagesParams.cs diff --git a/src/Discord.Net.Core/API/Rest/GetGatewayResponse.cs b/src/Discord.Net.API/Rest/GetGatewayResponse.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GetGatewayResponse.cs rename to src/Discord.Net.API/Rest/GetGatewayResponse.cs diff --git a/src/Discord.Net.Core/API/Rest/GetGuildMembersParams.cs b/src/Discord.Net.API/Rest/GetGuildMembersParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GetGuildMembersParams.cs rename to src/Discord.Net.API/Rest/GetGuildMembersParams.cs diff --git a/src/Discord.Net.Core/API/Rest/GetGuildPruneCountResponse.cs b/src/Discord.Net.API/Rest/GetGuildPruneCountResponse.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GetGuildPruneCountResponse.cs rename to src/Discord.Net.API/Rest/GetGuildPruneCountResponse.cs diff --git a/src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.API/Rest/GetReactionUsersParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GetReactionUsersParams.cs rename to src/Discord.Net.API/Rest/GetReactionUsersParams.cs diff --git a/src/Discord.Net.Core/API/Rest/GuildPruneParams.cs b/src/Discord.Net.API/Rest/GuildPruneParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/GuildPruneParams.cs rename to src/Discord.Net.API/Rest/GuildPruneParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyChannelPermissionsParams.cs b/src/Discord.Net.API/Rest/ModifyChannelPermissionsParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyChannelPermissionsParams.cs rename to src/Discord.Net.API/Rest/ModifyChannelPermissionsParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyCurrentUserNickParams.cs b/src/Discord.Net.API/Rest/ModifyCurrentUserNickParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyCurrentUserNickParams.cs rename to src/Discord.Net.API/Rest/ModifyCurrentUserNickParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs b/src/Discord.Net.API/Rest/ModifyCurrentUserParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyCurrentUserParams.cs rename to src/Discord.Net.API/Rest/ModifyCurrentUserParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildChannelParams.cs b/src/Discord.Net.API/Rest/ModifyGuildChannelParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildChannelParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildChannelParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildChannelsParams.cs b/src/Discord.Net.API/Rest/ModifyGuildChannelsParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildChannelsParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildChannelsParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildEmbedParams.cs b/src/Discord.Net.API/Rest/ModifyGuildEmbedParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildEmbedParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildEmbedParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildIntegrationParams.cs b/src/Discord.Net.API/Rest/ModifyGuildIntegrationParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildIntegrationParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildIntegrationParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildMemberParams.cs b/src/Discord.Net.API/Rest/ModifyGuildMemberParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildMemberParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildMemberParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildParams.cs b/src/Discord.Net.API/Rest/ModifyGuildParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.API/Rest/ModifyGuildRoleParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildRoleParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildRoleParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyGuildRolesParams.cs b/src/Discord.Net.API/Rest/ModifyGuildRolesParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyGuildRolesParams.cs rename to src/Discord.Net.API/Rest/ModifyGuildRolesParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs b/src/Discord.Net.API/Rest/ModifyMessageParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyMessageParams.cs rename to src/Discord.Net.API/Rest/ModifyMessageParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyTextChannelParams.cs b/src/Discord.Net.API/Rest/ModifyTextChannelParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyTextChannelParams.cs rename to src/Discord.Net.API/Rest/ModifyTextChannelParams.cs diff --git a/src/Discord.Net.Core/API/Rest/ModifyVoiceChannelParams.cs b/src/Discord.Net.API/Rest/ModifyVoiceChannelParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/ModifyVoiceChannelParams.cs rename to src/Discord.Net.API/Rest/ModifyVoiceChannelParams.cs diff --git a/src/Discord.Net.Core/API/Rest/UploadFileParams.cs b/src/Discord.Net.API/Rest/UploadFileParams.cs similarity index 100% rename from src/Discord.Net.Core/API/Rest/UploadFileParams.cs rename to src/Discord.Net.API/Rest/UploadFileParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs b/src/Discord.Net.API/Rpc/AuthenticateParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs rename to src/Discord.Net.API/Rpc/AuthenticateParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs b/src/Discord.Net.API/Rpc/AuthenticateResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs rename to src/Discord.Net.API/Rpc/AuthenticateResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs b/src/Discord.Net.API/Rpc/AuthorizeParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs rename to src/Discord.Net.API/Rpc/AuthorizeParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs b/src/Discord.Net.API/Rpc/AuthorizeResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs rename to src/Discord.Net.API/Rpc/AuthorizeResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/Channel.cs b/src/Discord.Net.API/Rpc/Channel.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/Channel.cs rename to src/Discord.Net.API/Rpc/Channel.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs b/src/Discord.Net.API/Rpc/ChannelSubscriptionParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs rename to src/Discord.Net.API/Rpc/ChannelSubscriptionParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs b/src/Discord.Net.API/Rpc/ChannelSummary.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs rename to src/Discord.Net.API/Rpc/ChannelSummary.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs b/src/Discord.Net.API/Rpc/ErrorEvent.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs rename to src/Discord.Net.API/Rpc/ErrorEvent.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs b/src/Discord.Net.API/Rpc/ExtendedVoiceState.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs rename to src/Discord.Net.API/Rpc/ExtendedVoiceState.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs b/src/Discord.Net.API/Rpc/GetChannelParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs rename to src/Discord.Net.API/Rpc/GetChannelParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs b/src/Discord.Net.API/Rpc/GetChannelsParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs rename to src/Discord.Net.API/Rpc/GetChannelsParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs b/src/Discord.Net.API/Rpc/GetChannelsResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs rename to src/Discord.Net.API/Rpc/GetChannelsResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs b/src/Discord.Net.API/Rpc/GetGuildParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs rename to src/Discord.Net.API/Rpc/GetGuildParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs b/src/Discord.Net.API/Rpc/GetGuildsParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs rename to src/Discord.Net.API/Rpc/GetGuildsParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs b/src/Discord.Net.API/Rpc/GetGuildsResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs rename to src/Discord.Net.API/Rpc/GetGuildsResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/Guild.cs b/src/Discord.Net.API/Rpc/Guild.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/Guild.cs rename to src/Discord.Net.API/Rpc/Guild.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs b/src/Discord.Net.API/Rpc/GuildMember.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GuildMember.cs rename to src/Discord.Net.API/Rpc/GuildMember.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs b/src/Discord.Net.API/Rpc/GuildStatusEvent.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs rename to src/Discord.Net.API/Rpc/GuildStatusEvent.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs b/src/Discord.Net.API/Rpc/GuildSubscriptionParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs rename to src/Discord.Net.API/Rpc/GuildSubscriptionParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs b/src/Discord.Net.API/Rpc/GuildSummary.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs rename to src/Discord.Net.API/Rpc/GuildSummary.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/Message.cs b/src/Discord.Net.API/Rpc/Message.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/Message.cs rename to src/Discord.Net.API/Rpc/Message.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs b/src/Discord.Net.API/Rpc/MessageEvent.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs rename to src/Discord.Net.API/Rpc/MessageEvent.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/Pan.cs b/src/Discord.Net.API/Rpc/Pan.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/Pan.cs rename to src/Discord.Net.API/Rpc/Pan.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs b/src/Discord.Net.API/Rpc/ReadyEvent.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs rename to src/Discord.Net.API/Rpc/ReadyEvent.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs b/src/Discord.Net.API/Rpc/RpcConfig.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs rename to src/Discord.Net.API/Rpc/RpcConfig.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs b/src/Discord.Net.API/Rpc/SelectChannelParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs rename to src/Discord.Net.API/Rpc/SelectChannelParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs b/src/Discord.Net.API/Rpc/SetLocalVolumeParams.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs rename to src/Discord.Net.API/Rpc/SetLocalVolumeParams.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs b/src/Discord.Net.API/Rpc/SetLocalVolumeResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs rename to src/Discord.Net.API/Rpc/SetLocalVolumeResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs b/src/Discord.Net.API/Rpc/SpeakingEvent.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs rename to src/Discord.Net.API/Rpc/SpeakingEvent.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs b/src/Discord.Net.API/Rpc/SubscriptionResponse.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs rename to src/Discord.Net.API/Rpc/SubscriptionResponse.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs b/src/Discord.Net.API/Rpc/UserVoiceSettings.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs rename to src/Discord.Net.API/Rpc/UserVoiceSettings.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceDevice.cs b/src/Discord.Net.API/Rpc/VoiceDevice.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/VoiceDevice.cs rename to src/Discord.Net.API/Rpc/VoiceDevice.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceDeviceSettings.cs b/src/Discord.Net.API/Rpc/VoiceDeviceSettings.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/VoiceDeviceSettings.cs rename to src/Discord.Net.API/Rpc/VoiceDeviceSettings.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceMode.cs b/src/Discord.Net.API/Rpc/VoiceMode.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/VoiceMode.cs rename to src/Discord.Net.API/Rpc/VoiceMode.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceSettings.cs b/src/Discord.Net.API/Rpc/VoiceSettings.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/VoiceSettings.cs rename to src/Discord.Net.API/Rpc/VoiceSettings.cs diff --git a/src/Discord.Net.Rpc/API/Rpc/VoiceShortcut.cs b/src/Discord.Net.API/Rpc/VoiceShortcut.cs similarity index 100% rename from src/Discord.Net.Rpc/API/Rpc/VoiceShortcut.cs rename to src/Discord.Net.API/Rpc/VoiceShortcut.cs diff --git a/src/Discord.Net.Rpc/Entities/VoiceShortcutType.cs b/src/Discord.Net.API/Rpc/VoiceShortcutType.cs similarity index 100% rename from src/Discord.Net.Rpc/Entities/VoiceShortcutType.cs rename to src/Discord.Net.API/Rpc/VoiceShortcutType.cs diff --git a/src/Discord.Net.Rpc/API/RpcFrame.cs b/src/Discord.Net.API/RpcFrame.cs similarity index 100% rename from src/Discord.Net.Rpc/API/RpcFrame.cs rename to src/Discord.Net.API/RpcFrame.cs diff --git a/src/Discord.Net.WebSocket/API/SocketFrame.cs b/src/Discord.Net.API/SocketFrame.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/SocketFrame.cs rename to src/Discord.Net.API/SocketFrame.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs b/src/Discord.Net.API/Voice/IdentifyParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs rename to src/Discord.Net.API/Voice/IdentifyParams.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs b/src/Discord.Net.API/Voice/ReadyEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs rename to src/Discord.Net.API/Voice/ReadyEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs b/src/Discord.Net.API/Voice/SelectProtocolParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs rename to src/Discord.Net.API/Voice/SelectProtocolParams.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs b/src/Discord.Net.API/Voice/SessionDescriptionEvent.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs rename to src/Discord.Net.API/Voice/SessionDescriptionEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs b/src/Discord.Net.API/Voice/SpeakingParams.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs rename to src/Discord.Net.API/Voice/SpeakingParams.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs b/src/Discord.Net.API/Voice/UdpProtocolInfo.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs rename to src/Discord.Net.API/Voice/UdpProtocolInfo.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs b/src/Discord.Net.API/Voice/VoiceOpCode.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs rename to src/Discord.Net.API/Voice/VoiceOpCode.cs diff --git a/src/Discord.Net.API/project.json b/src/Discord.Net.API/project.json new file mode 100644 index 000000000..41f09804d --- /dev/null +++ b/src/Discord.Net.API/project.json @@ -0,0 +1,38 @@ +{ + "version": "1.0.0-*", + "description": "A collection of Discord API definitions for use by Discord.Net", + "authors": [ "RogueException" ], + + "packOptions": { + "tags": [ "discord", "discordapp" ], + "licenseUrl": "http://opensource.org/licenses/MIT", + "projectUrl": "https://github.com/RogueException/Discord.Net", + "repository": { + "type": "git", + "url": "git://github.com/RogueException/Discord.Net" + } + }, + + "configurations": { + "Release": { + "buildOptions": { + "define": [ "RELEASE" ], + "nowarn": [ "CS1573", "CS1591" ], + "optimize": true, + "warningsAsErrors": true, + "xmlDoc": true + } + } + }, + + "dependencies": { + "Newtonsoft.Json": "9.0.1", + "System.Runtime": "4.3.0", + "System.Runtime.Serialization.Primitives": "4.1.1" + }, + + "frameworks": { + "netstandard1.1": {}, + "netstandard1.3": {} + } +} diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 9e993b624..a375d3255 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -9,12 +9,14 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord.Commands + diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 3cfdfa856..2fbb4a450 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -18,6 +18,6 @@ namespace Discord.Audio Task DisconnectAsync(); Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); - Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000); + Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000); } } diff --git a/src/Discord.Net.Core/API/CDN.cs b/src/Discord.Net.Core/CDN.cs similarity index 97% rename from src/Discord.Net.Core/API/CDN.cs rename to src/Discord.Net.Core/CDN.cs index e4fcbc8c4..98538e3c8 100644 --- a/src/Discord.Net.Core/API/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -1,4 +1,4 @@ -namespace Discord.API +namespace Discord { public static class CDN { diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index c64ed88c2..5ba8b7c0c 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,4 +1,4 @@ - + A .Net API wrapper and bot framework for Discord. 1.0.0-beta2 @@ -9,17 +9,26 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord + + + + + + + + diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index f9a65ae4c..6e7cf1f2a 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -1,4 +1,5 @@ using System.IO; +using Model = Discord.API.Image; namespace Discord { @@ -29,5 +30,10 @@ namespace Discord Stream = File.OpenRead(path); } #endif + + public Model ToModel() + { + return new Model(Stream); + } } } diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs index c517b88df..75e89d5ea 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs @@ -21,6 +21,6 @@ /// /// Your avatar /// - public Optional Avatar { get; set; } + public Optional Avatar { get; set; } } } diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index 4131b1796..a26f27dc0 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -1,4 +1,3 @@ -using Discord.API; using System; using System.Collections.Generic; using System.IO; @@ -9,7 +8,6 @@ namespace Discord public interface IDiscordClient : IDisposable { ConnectionState ConnectionState { get; } - DiscordRestApiClient ApiClient { get; } ISelfUser CurrentUser { get; } Task ConnectAsync(); diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json index 16a10f585..86caebfc1 100644 --- a/src/Discord.Net.Core/project.json +++ b/src/Discord.Net.Core/project.json @@ -26,11 +26,15 @@ }, "dependencies": { + "Discord.Net.API": { + "target": "project" + }, "Newtonsoft.Json": "9.0.1", "System.Collections.Concurrent": "4.3.0", "System.Collections.Immutable": "1.3.0", "System.Interactive.Async": "3.1.0", "System.Net.Http": "4.3.0", + "System.Runtime": "4.3.0", "System.Runtime.Serialization.Primitives": "4.1.1" }, diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index 963de8269..d1c44c11c 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -10,6 +10,7 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord.Providers.WS4Net diff --git a/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs index 7ab8685ba..93d6a83d6 100644 --- a/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs +++ b/src/Discord.Net.Providers.WS4Net/WS4NetClient.cs @@ -1,10 +1,10 @@ using Discord.Net.WebSockets; using System; +using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using System.Linq; using WebSocket4Net; using WS4NetSocket = WebSocket4Net.WebSocket; diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 87a719538..d589dbd4b 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -9,12 +9,14 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord.Rest + diff --git a/src/Discord.Net.Core/API/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs similarity index 99% rename from src/Discord.Net.Core/API/DiscordRestApiClient.cs rename to src/Discord.Net.Rest/DiscordRestApiClient.cs index bd27ed3be..07389f8df 100644 --- a/src/Discord.Net.Core/API/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -41,22 +41,22 @@ namespace Discord.API public RetryMode DefaultRetryMode { get; } public string UserAgent { get; } + internal RequestQueue RequestQueue { get; } public LoginState LoginState { get; private set; } public TokenType AuthTokenType { get; private set; } public User CurrentUser { get; private set; } - public RequestQueue RequestQueue { get; private set; } public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, - JsonSerializer serializer = null, RequestQueue requestQueue = null, bool fetchCurrentUser = true) + JsonSerializer serializer = null, bool fetchCurrentUser = true) { _restClientProvider = restClientProvider; UserAgent = userAgent; DefaultRetryMode = defaultRetryMode; _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; - RequestQueue = requestQueue; _fetchCurrentUser = fetchCurrentUser; + RequestQueue = new RequestQueue(); _stateLock = new SemaphoreSlim(1, 1); SetBaseUrl(DiscordConfig.ClientAPIUrl); diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index bf45db07f..d00a7e1fe 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -16,7 +16,7 @@ namespace Discord.Rest public DiscordRestClient(DiscordRestConfig config) : base(config, CreateApiClient(config)) { } private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) - => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, requestQueue: new RequestQueue()); + => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); protected override Task OnLoginAsync(TokenType tokenType, string token) { diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index a6a8ce9f3..f5218418d 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -32,10 +32,10 @@ namespace Discord.Rest return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(ITextChannel channel, BaseDiscordClient client, - Action func, + Action func, RequestOptions options) { - var args = new ModifyTextChannelParams(); + var args = new TextChannelProperties(); func(args); var apiArgs = new API.Rest.ModifyTextChannelParams { @@ -46,10 +46,10 @@ namespace Discord.Rest return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyAsync(IVoiceChannel channel, BaseDiscordClient client, - Action func, + Action func, RequestOptions options) { - var args = new ModifyVoiceChannelParams(); + var args = new VoiceChannelProperties(); func(args); var apiArgs = new API.Rest.ModifyVoiceChannelParams { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index c14899daa..1ce105e02 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -33,7 +33,7 @@ namespace Discord.Rest Topic = model.Topic.Value; } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index a19a5cb38..dd4f59deb 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -33,7 +33,7 @@ namespace Discord.Rest UserLimit = model.UserLimit.Value; } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 5c6f1cb0e..9dbb9eee9 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -27,9 +27,9 @@ namespace Discord.Rest AfkChannelId = args.AfkChannelId, AfkTimeout = args.AfkTimeout, DefaultMessageNotifications = args.DefaultMessageNotifications, - Icon = args.Icon.IsSpecified ? ImageModel.Create(args.Icon.Value) : Optional.Create(), + Icon = args.Icon.IsSpecified ? args.Icon.Value?.ToModel() : Optional.Create(), Name = args.Name, - Splash = args.Splash.IsSpecified ? ImageModel.Create(args.Splash.Value) : Optional.Create(), + Splash = args.Splash.IsSpecified ? args.Splash.Value?.ToModel() : Optional.Create(), Username = args.Username, VerificationLevel = args.VerificationLevel }; @@ -76,7 +76,7 @@ namespace Discord.Rest return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client, - IEnumerable args, RequestOptions options) + IEnumerable args, RequestOptions options) { var apiArgs = args.Select(x => new API.Rest.ModifyGuildChannelsParams(x.Id, x.Position)); await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, apiArgs, options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index a85107831..caabeeb92 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -35,8 +35,8 @@ namespace Discord.Rest public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public ulong DefaultChannelId => Id; - public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId); - public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, SplashId); + public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); + public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); public RestRole EveryoneRole => GetRole(Id); public IReadOnlyCollection Roles => _roles.ToReadOnlyCollection(); @@ -115,7 +115,7 @@ namespace Discord.Rest var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); } - public async Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) + public async Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) { var arr = args.ToArray(); await GuildHelper.ModifyChannelsAsync(this, Discord, arr, options); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs index 69c2c9362..cd214c2de 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs @@ -15,7 +15,7 @@ namespace Discord.Rest public GuildPermissions Permissions { get; private set; } public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); - public string IconUrl => API.CDN.GetGuildIconUrl(Id, _iconId); + public string IconUrl => CDN.GetGuildIconUrl(Id, _iconId); internal RestUserGuild(BaseDiscordClient discord, ulong id) : base(discord, id) diff --git a/src/Discord.Net.Rest/Entities/RestApplication.cs b/src/Discord.Net.Rest/Entities/RestApplication.cs index 62b434044..f81e4cd7b 100644 --- a/src/Discord.Net.Rest/Entities/RestApplication.cs +++ b/src/Discord.Net.Rest/Entities/RestApplication.cs @@ -18,7 +18,7 @@ namespace Discord.Rest public IUser Owner { get; private set; } public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); - public string IconUrl => API.CDN.GetApplicationIconUrl(Id, _iconId); + public string IconUrl => CDN.GetApplicationIconUrl(Id, _iconId); internal RestApplication(BaseDiscordClient discord, ulong id) : base(discord, id) diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 25419932f..5dada416c 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -13,7 +13,7 @@ namespace Discord.Rest public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId); + public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index e23c1cbb0..fd6079773 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -16,7 +16,7 @@ namespace Discord.Rest func(args); var apiArgs = new API.Rest.ModifyCurrentUserParams { - Avatar = args.Avatar.IsSpecified ? ImageModel.Create(args.Avatar.Value) : Optional.Create(), + Avatar = args.Avatar.IsSpecified ? args.Avatar.Value?.ToModel() : Optional.Create(), Username = args.Username }; diff --git a/src/Discord.Net.Core/Net/Queue/ClientBucket.cs b/src/Discord.Net.Rest/Net/Queue/ClientBucket.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/ClientBucket.cs rename to src/Discord.Net.Rest/Net/Queue/ClientBucket.cs diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs similarity index 99% rename from src/Discord.Net.Core/Net/Queue/RequestQueue.cs rename to src/Discord.Net.Rest/Net/Queue/RequestQueue.cs index 52ad90f11..ef7951765 100644 --- a/src/Discord.Net.Core/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs @@ -10,7 +10,7 @@ using System.Threading.Tasks; namespace Discord.Net.Queue { - public class RequestQueue : IDisposable + internal class RequestQueue : IDisposable { public event Func RateLimitTriggered; diff --git a/src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/RequestQueueBucket.cs rename to src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs diff --git a/src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs b/src/Discord.Net.Rest/Net/Queue/Requests/JsonRestRequest.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/Requests/JsonRestRequest.cs rename to src/Discord.Net.Rest/Net/Queue/Requests/JsonRestRequest.cs diff --git a/src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs b/src/Discord.Net.Rest/Net/Queue/Requests/MultipartRestRequest.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/Requests/MultipartRestRequest.cs rename to src/Discord.Net.Rest/Net/Queue/Requests/MultipartRestRequest.cs diff --git a/src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs b/src/Discord.Net.Rest/Net/Queue/Requests/RestRequest.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/Requests/RestRequest.cs rename to src/Discord.Net.Rest/Net/Queue/Requests/RestRequest.cs diff --git a/src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs b/src/Discord.Net.Rest/Net/Queue/Requests/WebSocketRequest.cs similarity index 100% rename from src/Discord.Net.Core/Net/Queue/Requests/WebSocketRequest.cs rename to src/Discord.Net.Rest/Net/Queue/Requests/WebSocketRequest.cs diff --git a/src/Discord.Net.Core/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs similarity index 96% rename from src/Discord.Net.Core/Net/RateLimitInfo.cs rename to src/Discord.Net.Rest/Net/RateLimitInfo.cs index 3c2b2acd5..5e87f664d 100644 --- a/src/Discord.Net.Core/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace Discord.Net { - public struct RateLimitInfo + internal struct RateLimitInfo { public bool IsGlobal { get; } public int? Limit { get; } diff --git a/src/Discord.Net.Rest/Utils/TypingNotifier.cs b/src/Discord.Net.Rest/Utils/TypingNotifier.cs index 45b715a76..b4bd2f44b 100644 --- a/src/Discord.Net.Rest/Utils/TypingNotifier.cs +++ b/src/Discord.Net.Rest/Utils/TypingNotifier.cs @@ -32,7 +32,7 @@ namespace Discord.Rest await _channel.TriggerTypingAsync(_options).ConfigureAwait(false); } catch { } - await Task.Delay(9750, token).ConfigureAwait(false); + await Task.Delay(9500, token).ConfigureAwait(false); } } catch (OperationCanceledException) { } diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 4b0ce4c5d..e69b5b66d 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -9,6 +9,7 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord.Rpc @@ -20,6 +21,7 @@ + @@ -29,9 +31,6 @@ - - - $(NoWarn);CS1573;CS1591 true diff --git a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs similarity index 99% rename from src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs rename to src/Discord.Net.Rpc/DiscordRpcApiClient.cs index ee6ad84e1..1348ccc7f 100644 --- a/src/Discord.Net.Rpc/API/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -69,14 +69,13 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } public DiscordRpcApiClient(string clientId, string userAgent, string origin, RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, - RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, RequestQueue requestQueue = null) - : base(restClientProvider, userAgent, defaultRetryMode, serializer, requestQueue, false) + RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) + : base(restClientProvider, userAgent, defaultRetryMode, serializer, false) { _connectionLock = new SemaphoreSlim(1, 1); _clientId = clientId; _origin = origin; - - _requestQueue = requestQueue ?? new RequestQueue(); + _requests = new ConcurrentDictionary(); _webSocketClient = webSocketProvider(); diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 9efa2d312..06096d8df 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -67,7 +67,7 @@ namespace Discord.Rpc } private static API.DiscordRpcApiClient CreateApiClient(string clientId, string origin, DiscordRpcConfig config) - => new API.DiscordRpcApiClient(clientId, DiscordRestConfig.UserAgent, origin, config.RestClientProvider, config.WebSocketProvider, requestQueue: new RequestQueue()); + => new API.DiscordRpcApiClient(clientId, DiscordRestConfig.UserAgent, origin, config.RestClientProvider, config.WebSocketProvider); /// public async Task ConnectAsync() diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index 360039beb..074286dbc 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -14,7 +14,7 @@ namespace Discord.Rpc public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId); + public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index a3774a76c..7443c569a 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json.Linq; using System; using System.IO; using System.Linq; -using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -180,10 +179,9 @@ namespace Discord.Audio { return new RTPWriteStream(this, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); } - public Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, - OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000) + public Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000) { - return new OpusEncodeStream(this, _secretKey, samplesPerFrame, _ssrc, bitrate, application, bufferSize); + return new OpusEncodeStream(this, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); } private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) diff --git a/src/Discord.Net.Core/Audio/Opus/OpusApplication.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs similarity index 100% rename from src/Discord.Net.Core/Audio/Opus/OpusApplication.cs rename to src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index 69d8b3d81..3806cc8bb 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -7,8 +7,7 @@ private readonly OpusEncoder _encoder; - internal OpusEncodeStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int? bitrate = null, - OpusApplication application = OpusApplication.MusicOrMixed, int bufferSize = 4000) + internal OpusEncodeStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int? bitrate = null, int bufferSize = 4000) : base(audioClient, secretKey, samplesPerFrame, ssrc, bufferSize) { _encoder = new OpusEncoder(SampleRate, Channels); diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index c7ca40c13..df16ad855 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -10,12 +10,14 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord.WebSocket + diff --git a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs similarity index 99% rename from src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs rename to src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 9592e2a04..1ab67d724 100644 --- a/src/Discord.Net.WebSocket/API/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -33,8 +33,8 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider, - RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, RequestQueue requestQueue = null) - : base(restClientProvider, userAgent, defaultRetryMode, serializer, requestQueue, true) + RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) + : base(restClientProvider, userAgent, defaultRetryMode, serializer, true) { _gatewayClient = webSocketProvider(); //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 0faa3cbbd..647847498 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -126,7 +126,7 @@ namespace Discord.WebSocket _largeGuilds = new ConcurrentQueue(); } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, config.WebSocketProvider, requestQueue: new RequestQueue()); + => new API.DiscordSocketApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, config.WebSocketProvider); protected override async Task OnLoginAsync(TokenType tokenType, string token) { @@ -1773,8 +1773,6 @@ namespace Discord.WebSocket } //IDiscordClient - DiscordRestApiClient IDiscordClient.ApiClient => ApiClient; - Task IDiscordClient.ConnectAsync() => ConnectAsync(); diff --git a/src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs similarity index 100% rename from src/Discord.Net.WebSocket/API/DiscordVoiceApiClient.cs rename to src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 674239be7..4819de751 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -15,7 +15,7 @@ namespace Discord.WebSocket internal abstract SocketGlobalUser GlobalUser { get; } internal abstract SocketPresence Presence { get; set; } - public string AvatarUrl => API.CDN.GetUserAvatarUrl(Id, AvatarId); + public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index be5f09125..4d9388915 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -9,12 +9,14 @@ http://opensource.org/licenses/MIT git git://github.com/RogueException/Discord.Net + Discord + From 572c0e6c6bf2ea0beffac70b6b6b30c4ba759fd9 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 15:22:59 -0400 Subject: [PATCH 110/263] Renamed new params classes to XXXProperties --- ...nelsParams.cs => BulkGuildChannelProperties.cs} | 4 ++-- ...dChannelParams.cs => GuildChannelProperties.cs} | 2 +- .../Entities/Channels/IGuildChannel.cs | 2 +- .../Entities/Channels/ITextChannel.cs | 2 +- .../Entities/Channels/IVoiceChannel.cs | 2 +- ...xtChannelParams.cs => TextChannelProperties.cs} | 2 +- ...eChannelParams.cs => VoiceChannelProperties.cs} | 2 +- ...GuildEmbedParams.cs => GuildEmbedProperties.cs} | 2 +- ...tionParams.cs => GuildIntegrationProperties.cs} | 2 +- .../{ModifyGuildParams.cs => GuildProperties.cs} | 2 +- src/Discord.Net.Core/Entities/Guilds/IGuild.cs | 8 ++++---- .../Entities/Messages/IUserMessage.cs | 2 +- ...ModifyMessageParams.cs => MessageProperties.cs} | 2 +- ...fyGuildRolesParams.cs => BulkRoleProperties.cs} | 4 ++-- src/Discord.Net.Core/Entities/Roles/IRole.cs | 2 +- ...{ModifyGuildRoleParams.cs => RoleProperties.cs} | 2 +- ...GuildMemberParams.cs => GuildUserProperties.cs} | 9 ++++++++- src/Discord.Net.Core/Entities/Users/IGuildUser.cs | 2 +- src/Discord.Net.Core/Entities/Users/ISelfUser.cs | 3 +-- .../Entities/Users/ModifyCurrentUserNickParams.cs | 12 ------------ ...yCurrentUserParams.cs => SelfUserProperties.cs} | 2 +- .../Entities/Channels/ChannelHelper.cs | 4 ++-- .../Entities/Channels/RestGuildChannel.cs | 2 +- .../Entities/Guilds/GuildHelper.cs | 12 ++++++------ src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs | 8 ++++---- .../Entities/Guilds/RestGuildIntegration.cs | 4 ++-- .../Entities/Messages/MessageHelper.cs | 4 ++-- .../Entities/Messages/RestUserMessage.cs | 2 +- src/Discord.Net.Rest/Entities/Roles/RestRole.cs | 2 +- src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs | 4 ++-- .../Entities/Users/RestGuildUser.cs | 2 +- .../Entities/Users/RestSelfUser.cs | 2 +- src/Discord.Net.Rest/Entities/Users/UserHelper.cs | 14 +++++++++----- .../Entities/Channels/RpcGuildChannel.cs | 2 +- .../Entities/Channels/RpcTextChannel.cs | 2 +- .../Entities/Channels/RpcVoiceChannel.cs | 2 +- .../Entities/Messages/RpcUserMessage.cs | 2 +- .../Entities/Channels/SocketGuildChannel.cs | 2 +- .../Entities/Channels/SocketTextChannel.cs | 2 +- .../Entities/Channels/SocketVoiceChannel.cs | 2 +- .../Entities/Guilds/SocketGuild.cs | 12 ++++++------ .../Entities/Messages/SocketUserMessage.cs | 2 +- .../Entities/Roles/SocketRole.cs | 2 +- .../Entities/Users/SocketGuildUser.cs | 2 +- .../Entities/Users/SocketSelfUser.cs | 2 +- 45 files changed, 81 insertions(+), 83 deletions(-) rename src/Discord.Net.Core/Entities/Channels/{ModifyGuildChannelsParams.cs => BulkGuildChannelProperties.cs} (78%) rename src/Discord.Net.Core/Entities/Channels/{ModifyGuildChannelParams.cs => GuildChannelProperties.cs} (95%) rename src/Discord.Net.Core/Entities/Channels/{ModifyTextChannelParams.cs => TextChannelProperties.cs} (76%) rename src/Discord.Net.Core/Entities/Channels/{ModifyVoiceChannelParams.cs => VoiceChannelProperties.cs} (85%) rename src/Discord.Net.Core/Entities/Guilds/{ModifyGuildEmbedParams.cs => GuildEmbedProperties.cs} (94%) rename src/Discord.Net.Core/Entities/Guilds/{ModifyGuildIntegrationParams.cs => GuildIntegrationProperties.cs} (82%) rename src/Discord.Net.Core/Entities/Guilds/{ModifyGuildParams.cs => GuildProperties.cs} (98%) rename src/Discord.Net.Core/Entities/Messages/{ModifyMessageParams.cs => MessageProperties.cs} (96%) rename src/Discord.Net.Core/Entities/Roles/{ModifyGuildRolesParams.cs => BulkRoleProperties.cs} (64%) rename src/Discord.Net.Core/Entities/Roles/{ModifyGuildRoleParams.cs => RoleProperties.cs} (97%) rename src/Discord.Net.Core/Entities/Users/{ModifyGuildMemberParams.cs => GuildUserProperties.cs} (88%) delete mode 100644 src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs rename src/Discord.Net.Core/Entities/Users/{ModifyCurrentUserParams.cs => SelfUserProperties.cs} (94%) diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs b/src/Discord.Net.Core/Entities/Channels/BulkGuildChannelProperties.cs similarity index 78% rename from src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs rename to src/Discord.Net.Core/Entities/Channels/BulkGuildChannelProperties.cs index 14b77457f..2358b2e2e 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelsParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/BulkGuildChannelProperties.cs @@ -1,6 +1,6 @@ namespace Discord { - public class ModifyGuildChannelsParams + public class BulkGuildChannelProperties { /// /// The id of the channel to apply this position to. @@ -11,7 +11,7 @@ /// public int Position { get; set; } - public ModifyGuildChannelsParams(ulong id, int position) + public BulkGuildChannelProperties(ulong id, int position) { Id = id; Position = position; diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs similarity index 95% rename from src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs rename to src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs index 005460915..0ea196a4a 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyGuildChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/GuildChannelProperties.cs @@ -11,7 +11,7 @@ /// }); /// /// - public class ModifyGuildChannelParams + public class GuildChannelProperties { /// /// Set the channel to this name diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 2e9272729..893f5726a 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -26,7 +26,7 @@ namespace Discord Task> GetInvitesAsync(RequestOptions options = null); /// Modifies this guild channel. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); /// Gets the permission overwrite for a specific role, or null if one does not exist. OverwritePermissions? GetPermissionOverwrite(IRole role); diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 7ecaf6d7b..9a89a6fa4 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -10,6 +10,6 @@ namespace Discord string Topic { get; } /// Modifies this text channel. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); } } \ No newline at end of file diff --git a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs index d1be73072..dfcb226de 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -13,7 +13,7 @@ namespace Discord int UserLimit { get; } /// Modifies this voice channel. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); /// Connects to this voice channel. Task ConnectAsync(); } diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs similarity index 76% rename from src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs rename to src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs index d144706d9..2461a09f2 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyTextChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/TextChannelProperties.cs @@ -1,7 +1,7 @@ namespace Discord { /// - public class ModifyTextChannelParams : ModifyGuildChannelParams + public class TextChannelProperties : GuildChannelProperties { /// /// What the topic of the channel should be set to. diff --git a/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs b/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs similarity index 85% rename from src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs rename to src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs index bb3d2fee5..3294ad146 100644 --- a/src/Discord.Net.Core/Entities/Channels/ModifyVoiceChannelParams.cs +++ b/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs @@ -1,7 +1,7 @@ namespace Discord { /// - public class ModifyVoiceChannelParams : ModifyGuildChannelParams + public class VoiceChannelProperties : GuildChannelProperties { /// /// The bitrate of the voice connections in this channel. Must be greater than 8000 diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs b/src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs similarity index 94% rename from src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs rename to src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs index f5d212e17..a2b2ec4fc 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildEmbedParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildEmbedProperties.cs @@ -3,7 +3,7 @@ /// /// Modify the widget of an IGuild with the specified parameters /// - public class ModifyGuildEmbedParams + public class GuildEmbedProperties { /// /// Should the widget be enabled? diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs b/src/Discord.Net.Core/Entities/Guilds/GuildIntegrationProperties.cs similarity index 82% rename from src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs rename to src/Discord.Net.Core/Entities/Guilds/GuildIntegrationProperties.cs index b9375d907..f329e78e6 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildIntegrationParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildIntegrationProperties.cs @@ -1,6 +1,6 @@ namespace Discord { - public class ModifyGuildIntegrationParams + public class GuildIntegrationProperties { public Optional ExpireBehavior { get; set; } public Optional ExpireGracePeriod { get; set; } diff --git a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs similarity index 98% rename from src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs rename to src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs index ec7866b8a..ebec03e0b 100644 --- a/src/Discord.Net.Core/Entities/Guilds/ModifyGuildParams.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildProperties.cs @@ -13,7 +13,7 @@ /// /// /// - public class ModifyGuildParams + public class GuildProperties { public Optional Username { get; set; } /// diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 413b5da62..a324248f5 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -53,13 +53,13 @@ namespace Discord IReadOnlyCollection Roles { get; } /// Modifies this guild. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); /// Modifies this guild's embed. - Task ModifyEmbedAsync(Action func, RequestOptions options = null); + Task ModifyEmbedAsync(Action func, RequestOptions options = null); /// Bulk modifies the channels of this guild. - Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null); + Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null); /// Bulk modifies the roles of this guild. - Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null); + Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null); /// Leaves this guild. If you are the owner, use Delete instead. Task LeaveAsync(RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 346f1a45e..73d402041 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -7,7 +7,7 @@ namespace Discord public interface IUserMessage : IMessage { /// Modifies this message. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); /// Adds this message to its channel's pinned messages. Task PinAsync(RequestOptions options = null); /// Removes this message from its channel's pinned messages. diff --git a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs similarity index 96% rename from src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs rename to src/Discord.Net.Core/Entities/Messages/MessageProperties.cs index 6b7e3b478..df8c51dea 100644 --- a/src/Discord.Net.Core/Entities/Messages/ModifyMessageParams.cs +++ b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs @@ -20,7 +20,7 @@ /// }); /// /// - public class ModifyMessageParams + public class MessageProperties { /// /// The content of the message diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs b/src/Discord.Net.Core/Entities/Roles/BulkRoleProperties.cs similarity index 64% rename from src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs rename to src/Discord.Net.Core/Entities/Roles/BulkRoleProperties.cs index 5140f4dae..eacb6689d 100644 --- a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRolesParams.cs +++ b/src/Discord.Net.Core/Entities/Roles/BulkRoleProperties.cs @@ -1,13 +1,13 @@ namespace Discord { - public class ModifyGuildRolesParams : ModifyGuildRoleParams + public class BulkRoleProperties : RoleProperties { /// /// The id of the role to be edited /// public ulong Id { get; } - public ModifyGuildRolesParams(ulong id) + public BulkRoleProperties(ulong id) { Id = id; } diff --git a/src/Discord.Net.Core/Entities/Roles/IRole.cs b/src/Discord.Net.Core/Entities/Roles/IRole.cs index 2e3d63702..54cc5d1ee 100644 --- a/src/Discord.Net.Core/Entities/Roles/IRole.cs +++ b/src/Discord.Net.Core/Entities/Roles/IRole.cs @@ -25,6 +25,6 @@ namespace Discord int Position { get; } ///// Modifies this role. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs b/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs similarity index 97% rename from src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs rename to src/Discord.Net.Core/Entities/Roles/RoleProperties.cs index bdd1c41e9..8950a2634 100644 --- a/src/Discord.Net.Core/Entities/Roles/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Core/Entities/Roles/RoleProperties.cs @@ -13,7 +13,7 @@ /// /// /// - public class ModifyGuildRoleParams + public class RoleProperties { /// /// The name of the role diff --git a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs b/src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs similarity index 88% rename from src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs rename to src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs index 50f535a4c..5ceffef0e 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyGuildMemberParams.cs +++ b/src/Discord.Net.Core/Entities/Users/GuildUserProperties.cs @@ -14,7 +14,7 @@ namespace Discord /// /// /// - public class ModifyGuildMemberParams + public class GuildUserProperties { /// /// Should the user be guild-muted in a voice channel? @@ -60,5 +60,12 @@ namespace Discord /// This user MUST already be in a Voice Channel for this to work. /// public Optional Channel { get; set; } + /// + /// Move a user to a voice channel. + /// + /// + /// This user MUST already be in a Voice Channel for this to work. + /// + public Optional ChannelId { get; set; } } } diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index ab447e520..d31c4fefb 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -28,6 +28,6 @@ namespace Discord /// Kicks this user from this guild. Task KickAsync(RequestOptions options = null); /// Modifies this user's properties in this guild. - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); } } diff --git a/src/Discord.Net.Core/Entities/Users/ISelfUser.cs b/src/Discord.Net.Core/Entities/Users/ISelfUser.cs index 782ea22c7..7b91d4e3a 100644 --- a/src/Discord.Net.Core/Entities/Users/ISelfUser.cs +++ b/src/Discord.Net.Core/Entities/Users/ISelfUser.cs @@ -1,4 +1,3 @@ -using Discord.API.Rest; using System; using System.Threading.Tasks; @@ -13,6 +12,6 @@ namespace Discord /// Returns true if this user has enabled MFA on their account. bool IsMfaEnabled { get; } - Task ModifyAsync(Action func, RequestOptions options = null); + Task ModifyAsync(Action func, RequestOptions options = null); } } \ No newline at end of file diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs b/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs deleted file mode 100644 index c8d2b5199..000000000 --- a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserNickParams.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Discord -{ - public class ModifyCurrentUserNickParams - { - public string Nickname { get; } - - public ModifyCurrentUserNickParams(string nickname) - { - Nickname = nickname; - } - } -} diff --git a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs b/src/Discord.Net.Core/Entities/Users/SelfUserProperties.cs similarity index 94% rename from src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs rename to src/Discord.Net.Core/Entities/Users/SelfUserProperties.cs index 75e89d5ea..9c4162780 100644 --- a/src/Discord.Net.Core/Entities/Users/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Core/Entities/Users/SelfUserProperties.cs @@ -12,7 +12,7 @@ /// /// /// - public class ModifyCurrentUserParams + public class SelfUserProperties { /// /// Your username diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index f5218418d..e97a59ec5 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -19,10 +19,10 @@ namespace Discord.Rest await client.ApiClient.DeleteChannelAsync(channel.Id, options).ConfigureAwait(false); } public static async Task ModifyAsync(IGuildChannel channel, BaseDiscordClient client, - Action func, + Action func, RequestOptions options) { - var args = new ModifyGuildChannelParams(); + var args = new GuildChannelProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildChannelParams { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 0e6e55772..dc7b3250a 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -54,7 +54,7 @@ namespace Discord.Rest var model = await Discord.ApiClient.GetChannelAsync(GuildId, Id, options).ConfigureAwait(false); Update(model); } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await ChannelHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 9dbb9eee9..195ae27d0 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -15,11 +15,11 @@ namespace Discord.Rest { //General public static async Task ModifyAsync(IGuild guild, BaseDiscordClient client, - Action func, RequestOptions options) + Action func, RequestOptions options) { if (func == null) throw new NullReferenceException(nameof(func)); - var args = new ModifyGuildParams(); + var args = new GuildProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildParams @@ -57,11 +57,11 @@ namespace Discord.Rest return await client.ApiClient.ModifyGuildAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyEmbedAsync(IGuild guild, BaseDiscordClient client, - Action func, RequestOptions options) + Action func, RequestOptions options) { if (func == null) throw new NullReferenceException(nameof(func)); - var args = new ModifyGuildEmbedParams(); + var args = new GuildEmbedProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildEmbedParams { @@ -76,13 +76,13 @@ namespace Discord.Rest return await client.ApiClient.ModifyGuildEmbedAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task ModifyChannelsAsync(IGuild guild, BaseDiscordClient client, - IEnumerable args, RequestOptions options) + IEnumerable args, RequestOptions options) { var apiArgs = args.Select(x => new API.Rest.ModifyGuildChannelsParams(x.Id, x.Position)); await client.ApiClient.ModifyGuildChannelsAsync(guild.Id, apiArgs, options).ConfigureAwait(false); } public static async Task> ModifyRolesAsync(IGuild guild, BaseDiscordClient client, - IEnumerable args, RequestOptions options) + IEnumerable args, RequestOptions options) { var apiArgs = args.Select(x => new API.Rest.ModifyGuildRolesParams(x.Id) { diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index caabeeb92..3e550e452 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -105,22 +105,22 @@ namespace Discord.Rest public Task DeleteAsync(RequestOptions options = null) => GuildHelper.DeleteAsync(this, Discord, options); - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await GuildHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); } - public async Task ModifyEmbedAsync(Action func, RequestOptions options = null) + public async Task ModifyEmbedAsync(Action func, RequestOptions options = null) { var model = await GuildHelper.ModifyEmbedAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); } - public async Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) + public async Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) { var arr = args.ToArray(); await GuildHelper.ModifyChannelsAsync(this, Discord, arr, options); } - public async Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null) + public async Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null) { var models = await GuildHelper.ModifyRolesAsync(this, Discord, args, options).ConfigureAwait(false); foreach (var model in models) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs index 5405becc4..b4b373338 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs @@ -55,11 +55,11 @@ namespace Discord.Rest { await Discord.ApiClient.DeleteGuildIntegrationAsync(GuildId, Id).ConfigureAwait(false); } - public async Task ModifyAsync(Action func) + public async Task ModifyAsync(Action func) { if (func == null) throw new NullReferenceException(nameof(func)); - var args = new ModifyGuildIntegrationParams(); + var args = new GuildIntegrationProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildIntegrationParams { diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 4f74129dd..f3aa70676 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -10,10 +10,10 @@ namespace Discord.Rest { internal static class MessageHelper { - public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action func, + public static async Task ModifyAsync(IMessage msg, BaseDiscordClient client, Action func, RequestOptions options) { - var args = new ModifyMessageParams(); + var args = new MessageProperties(); func(args); var apiArgs = new API.Rest.ModifyMessageParams { diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index b49aa3c67..4ae25a142 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -126,7 +126,7 @@ namespace Discord.Rest } } - public async Task ModifyAsync(Action func, RequestOptions options) + public async Task ModifyAsync(Action func, RequestOptions options) { var model = await MessageHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs index eee1fdf0a..e39dea1a1 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs @@ -44,7 +44,7 @@ namespace Discord.Rest Permissions = new GuildPermissions(model.Permissions); } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await RoleHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index 07836c993..d6b7c597f 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -14,9 +14,9 @@ namespace Discord.Rest await client.ApiClient.DeleteGuildRoleAsync(role.Guild.Id, role.Id, options).ConfigureAwait(false); } public static async Task ModifyAsync(IRole role, BaseDiscordClient client, - Action func, RequestOptions options) + Action func, RequestOptions options) { - var args = new ModifyGuildRoleParams(); + var args = new RoleProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildRoleParams { diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index 40d57c511..ee3551fd5 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -68,7 +68,7 @@ namespace Discord.Rest var model = await Discord.ApiClient.GetGuildMemberAsync(GuildId, Id, options).ConfigureAwait(false); Update(model); } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var args = await UserHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); if (args.Deaf.IsSpecified) diff --git a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs index c97443522..3afaff777 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs @@ -43,7 +43,7 @@ namespace Discord.Rest Update(model); } - public async Task ModifyAsync(Action func, RequestOptions options = null) + public async Task ModifyAsync(Action func, RequestOptions options = null) { if (Id != Discord.CurrentUser.Id) throw new InvalidOperationException("Unable to modify this object using a different token."); diff --git a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs index fd6079773..5189851fd 100644 --- a/src/Discord.Net.Rest/Entities/Users/UserHelper.cs +++ b/src/Discord.Net.Rest/Entities/Users/UserHelper.cs @@ -9,10 +9,10 @@ namespace Discord.Rest { internal static class UserHelper { - public static async Task ModifyAsync(ISelfUser user, BaseDiscordClient client, Action func, + public static async Task ModifyAsync(ISelfUser user, BaseDiscordClient client, Action func, RequestOptions options) { - var args = new ModifyCurrentUserParams(); + var args = new SelfUserProperties(); func(args); var apiArgs = new API.Rest.ModifyCurrentUserParams { @@ -25,19 +25,23 @@ namespace Discord.Rest return await client.ApiClient.ModifySelfAsync(apiArgs, options).ConfigureAwait(false); } - public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action func, + public static async Task ModifyAsync(IGuildUser user, BaseDiscordClient client, Action func, RequestOptions options) { - var args = new ModifyGuildMemberParams(); + var args = new GuildUserProperties(); func(args); var apiArgs = new API.Rest.ModifyGuildMemberParams { - ChannelId = args.Channel.IsSpecified ? args.Channel.Value.Id : Optional.Create(), Deaf = args.Deaf, Mute = args.Mute, Nickname = args.Nickname }; + if (args.Channel.IsSpecified) + apiArgs.ChannelId = args.Channel.Value.Id; + else if (args.ChannelId.IsSpecified) + apiArgs.ChannelId = args.ChannelId.Value; + if (args.Roles.IsSpecified) apiArgs.RoleIds = args.Roles.Value.Select(x => x.Id).ToArray(); else if (args.RoleIds.IsSpecified) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 8a0ac2a9e..775798677 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -36,7 +36,7 @@ namespace Discord.Rpc Position = model.Position.Value; } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); public Task DeleteAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs index 08780debb..0023ccdc1 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -34,7 +34,7 @@ namespace Discord.Rpc CachedMessages = model.Messages.Select(x => RpcMessage.Create(Discord, Id, x)).ToImmutableArray(); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); //TODO: Use RPC cache diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs index 1e6510a38..ed05e2f57 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs @@ -38,7 +38,7 @@ namespace Discord.Rpc VoiceStates = model.VoiceStates.Select(x => RpcVoiceState.Create(Discord, x)).ToImmutableArray(); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index c03dd7ab7..bf5ad6510 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -99,7 +99,7 @@ namespace Discord.Rpc } } - public Task ModifyAsync(Action func, RequestOptions options) + public Task ModifyAsync(Action func, RequestOptions options) => MessageHelper.ModifyAsync(this, Discord, func, options); public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 45d58b42c..be99f3f9f 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -51,7 +51,7 @@ namespace Discord.WebSocket _overwrites = newOverwrites.ToImmutable(); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); public Task DeleteAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 903263e89..4b1ec4ae8 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -44,7 +44,7 @@ namespace Discord.WebSocket Topic = model.Topic.Value; } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); //Messages diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index b311f3c01..a2c1e217b 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -38,7 +38,7 @@ namespace Discord.WebSocket UserLimit = model.UserLimit.Value; } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); public override SocketGuildUser GetUser(ulong id) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index b63b47403..7ed5bf8f9 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -53,8 +53,8 @@ namespace Discord.WebSocket public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public ulong DefaultChannelId => Id; - public string IconUrl => API.CDN.GetGuildIconUrl(Id, IconId); - public string SplashUrl => API.CDN.GetGuildSplashUrl(Id, SplashId); + public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); + public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); public bool HasAllMembers => _downloaderPromise.Task.IsCompleted; public bool IsSynced => _syncPromise.Task.IsCompleted; public Task SyncPromise => _syncPromise.Task; @@ -252,13 +252,13 @@ namespace Discord.WebSocket public Task DeleteAsync(RequestOptions options = null) => GuildHelper.DeleteAsync(this, Discord, options); - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => GuildHelper.ModifyAsync(this, Discord, func, options); - public Task ModifyEmbedAsync(Action func, RequestOptions options = null) + public Task ModifyEmbedAsync(Action func, RequestOptions options = null) => GuildHelper.ModifyEmbedAsync(this, Discord, func, options); - public Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) + public Task ModifyChannelsAsync(IEnumerable args, RequestOptions options = null) => GuildHelper.ModifyChannelsAsync(this, Discord, args, options); - public Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null) + public Task ModifyRolesAsync(IEnumerable args, RequestOptions options = null) => GuildHelper.ModifyRolesAsync(this, Discord, args, options); public Task LeaveAsync(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 6184939dd..e50076986 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -125,7 +125,7 @@ namespace Discord.WebSocket _reactions.Clear(); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => MessageHelper.ModifyAsync(this, Discord, func, options); public Task AddReactionAsync(Emoji emoji, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs index 5fa585138..ce46aa24f 100644 --- a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs +++ b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs @@ -46,7 +46,7 @@ namespace Discord.WebSocket Permissions = new GuildPermissions(model.Permissions); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => RoleHelper.ModifyAsync(this, Discord, func, options); public Task DeleteAsync(RequestOptions options = null) => RoleHelper.DeleteAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 3ef45d230..720f39c04 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -102,7 +102,7 @@ namespace Discord.WebSocket _roleIds = roles.ToImmutable(); } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => UserHelper.ModifyAsync(this, Discord, func, options); public Task KickAsync(RequestOptions options = null) => UserHelper.KickAsync(this, Discord, options); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs index 9b85e15e0..fbfdc4cbe 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs @@ -44,7 +44,7 @@ namespace Discord.WebSocket IsMfaEnabled = model.MfaEnabled.Value; } - public Task ModifyAsync(Action func, RequestOptions options = null) + public Task ModifyAsync(Action func, RequestOptions options = null) => UserHelper.ModifyAsync(this, Discord, func, options); internal new SocketSelfUser Clone() => MemberwiseClone() as SocketSelfUser; From 1965c0539aa7c0a0faa38a447f7eb3ad8705efd2 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 16:03:19 -0400 Subject: [PATCH 111/263] Added support for concrete Rest/Socket/RPC classes in command params --- .../Builders/ModuleClassBuilder.cs | 18 ++----- src/Discord.Net.Commands/CommandService.cs | 51 +++++++++++-------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 7351c7e57..1333edd78 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -215,20 +215,6 @@ namespace Discord.Commands else reader = service.GetDefaultTypeReader(paramType); - if (reader == null) - { - var paramTypeInfo = paramType.GetTypeInfo(); - if (paramTypeInfo.IsEnum) - { - reader = EnumTypeReader.GetReader(paramType); - service.AddTypeReader(paramType, reader); - } - else - { - throw new InvalidOperationException($"{paramType.FullName} is not supported as a command parameter, are you missing a TypeReader?"); - } - } - builder.ParameterType = paramType; builder.TypeReader = reader; } @@ -239,10 +225,12 @@ namespace Discord.Commands var readers = service.GetTypeReaders(paramType); TypeReader reader = null; if (readers != null) + { if (readers.TryGetValue(typeReaderType, out reader)) return reader; + } - //could not find any registered type reader: try to create one + //We dont have a cached type reader, create one reader = ReflectionUtils.CreateObject(typeReaderType.GetTypeInfo(), service, DependencyMap.Empty); service.AddTypeReader(paramType, reader); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 7152b87da..18c6c3426 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -17,6 +17,7 @@ namespace Discord.Commands private readonly ConcurrentDictionary _typedModuleDefs; private readonly ConcurrentDictionary> _typeReaders; private readonly ConcurrentDictionary _defaultTypeReaders; + private readonly ImmutableList> _entityTypeReaders; //TODO: Candidate for C#7 Tuple private readonly ConcurrentBag _moduleDefs; private readonly CommandMap _map; @@ -41,27 +42,16 @@ namespace Discord.Commands _map = new CommandMap(this); _typeReaders = new ConcurrentDictionary>(); - _defaultTypeReaders = new ConcurrentDictionary - { - [typeof(IMessage)] = new MessageTypeReader(), - [typeof(IUserMessage)] = new MessageTypeReader(), - [typeof(IChannel)] = new ChannelTypeReader(), - [typeof(IDMChannel)] = new ChannelTypeReader(), - [typeof(IGroupChannel)] = new ChannelTypeReader(), - [typeof(IGuildChannel)] = new ChannelTypeReader(), - [typeof(IMessageChannel)] = new ChannelTypeReader(), - [typeof(IPrivateChannel)] = new ChannelTypeReader(), - [typeof(ITextChannel)] = new ChannelTypeReader(), - [typeof(IVoiceChannel)] = new ChannelTypeReader(), - - [typeof(IRole)] = new RoleTypeReader(), - - [typeof(IUser)] = new UserTypeReader(), - [typeof(IGroupUser)] = new UserTypeReader(), - [typeof(IGuildUser)] = new UserTypeReader(), - }; + _defaultTypeReaders = new ConcurrentDictionary(); foreach (var type in PrimitiveParsers.SupportedTypes) _defaultTypeReaders[type] = PrimitiveTypeReader.Create(type); + + var entityTypeReaders = ImmutableList.CreateBuilder>(); + entityTypeReaders.Add(new Tuple(typeof(IMessage), typeof(MessageTypeReader<>))); + entityTypeReaders.Add(new Tuple(typeof(IChannel), typeof(ChannelTypeReader<>))); + entityTypeReaders.Add(new Tuple(typeof(IRole), typeof(RoleTypeReader<>))); + entityTypeReaders.Add(new Tuple(typeof(IUser), typeof(UserTypeReader<>))); + _entityTypeReaders = entityTypeReaders.ToImmutable(); } //Modules @@ -208,11 +198,32 @@ namespace Discord.Commands TypeReader reader; if (_defaultTypeReaders.TryGetValue(type, out reader)) return reader; + var typeInfo = type.GetTypeInfo(); + + //Is this an enum? + if (typeInfo.IsEnum) + { + reader = EnumTypeReader.GetReader(type); + _defaultTypeReaders[type] = reader; + return reader; + } + + //Is this an entity? + for (int i = 0; i < _entityTypeReaders.Count; i++) + { + if (type == _entityTypeReaders[i].Item1 || typeInfo.ImplementedInterfaces.Contains(_entityTypeReaders[i].Item1)) + { + reader = Activator.CreateInstance(_entityTypeReaders[i].Item2.MakeGenericType(type)) as TypeReader; + _defaultTypeReaders[type] = reader; + return reader; + } + } return null; } //Execution - public SearchResult Search(CommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); + public SearchResult Search(CommandContext context, int argPos) + => Search(context, context.Message.Content.Substring(argPos)); public SearchResult Search(CommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); From 2c075e186af051f7447db822e1f57a7a4f3a128e Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 23 Dec 2016 16:52:32 -0400 Subject: [PATCH 112/263] Added support for custom ModuleBase command contexts. Added SocketCommandContext/RpcCommandContext. --- .../ParameterPreconditionAttribute.cs | 2 +- .../Attributes/PreconditionAttribute.cs | 2 +- .../RequireBotPermissionAttribute.cs | 2 +- .../Preconditions/RequireContextAttribute.cs | 2 +- .../Preconditions/RequireOwnerAttribute.cs | 2 +- .../RequireUserPermissionAttribute.cs | 2 +- .../Builders/CommandBuilder.cs | 4 +-- .../Builders/ModuleBuilder.cs | 2 +- .../Builders/ModuleClassBuilder.cs | 6 ++-- src/Discord.Net.Commands/CommandContext.cs | 12 ++----- src/Discord.Net.Commands/CommandMatch.cs | 8 ++--- src/Discord.Net.Commands/CommandParser.cs | 2 +- src/Discord.Net.Commands/CommandService.cs | 8 ++--- src/Discord.Net.Commands/IModuleBase.cs | 7 +++++ src/Discord.Net.Commands/Info/CommandInfo.cs | 10 +++--- .../Info/ParameterInfo.cs | 4 +-- src/Discord.Net.Commands/ModuleBase.cs | 19 ++++++++++-- .../Readers/ChannelTypeReader.cs | 2 +- .../Readers/EnumTypeReader.cs | 2 +- .../Readers/MessageTypeReader.cs | 2 +- .../Readers/PrimitiveTypeReader.cs | 2 +- .../Readers/RoleTypeReader.cs | 2 +- .../Readers/TypeReader.cs | 2 +- .../Readers/UserTypeReader.cs | 2 +- .../Commands/ICommandContext.cs | 11 +++++++ src/Discord.Net.Rest/Discord.Net.Rest.csproj | 2 +- ...Channel.cs => RpcVirtualMessageChannel.cs} | 0 .../Commands/RpcCommandContext.cs | 29 +++++++++++++++++ .../Entities/Messages/RpcMessage.cs | 6 ++-- .../Entities/Messages/RpcSystemMessage.cs | 2 +- .../Entities/Messages/RpcUserMessage.cs | 2 +- .../Commands/SocketCommandContext.cs | 31 +++++++++++++++++++ 32 files changed, 138 insertions(+), 53 deletions(-) create mode 100644 src/Discord.Net.Commands/IModuleBase.cs create mode 100644 src/Discord.Net.Core/Commands/ICommandContext.cs rename src/Discord.Net.Rest/Entities/Channels/{RestVirtualMessageChannel.cs => RpcVirtualMessageChannel.cs} (100%) create mode 100644 src/Discord.Net.Rpc/Commands/RpcCommandContext.cs create mode 100644 src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs diff --git a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs index 3bf8d177a..168d15e5f 100644 --- a/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/ParameterPreconditionAttribute.cs @@ -6,6 +6,6 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = true, Inherited = true)] public abstract class ParameterPreconditionAttribute : Attribute { - public abstract Task CheckPermissions(CommandContext context, ParameterInfo parameter, object value, IDependencyMap map); + public abstract Task CheckPermissions(ICommandContext context, ParameterInfo parameter, object value, IDependencyMap map); } } \ No newline at end of file diff --git a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs index 067c8e93b..7755d459b 100644 --- a/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/PreconditionAttribute.cs @@ -6,6 +6,6 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public abstract class PreconditionAttribute : Attribute { - public abstract Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map); + public abstract Task CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map); } } diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs index 9f0e3c905..520cfa6fd 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireBotPermissionAttribute.cs @@ -41,7 +41,7 @@ namespace Discord.Commands GuildPermission = null; } - public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map) { var guildUser = await context.Guild.GetCurrentUserAsync(); diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs index bc5d9af58..42d835c30 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireContextAttribute.cs @@ -37,7 +37,7 @@ namespace Discord.Commands Contexts = contexts; } - public override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + public override Task CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map) { bool isValid = false; diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs index 5b290954c..cfedcad23 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireOwnerAttribute.cs @@ -10,7 +10,7 @@ namespace Discord.Commands [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class RequireOwnerAttribute : PreconditionAttribute { - public override async Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + public override async Task CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map) { var application = await context.Client.GetApplicationInfoAsync(); if (context.User.Id == application.Owner.Id) return PreconditionResult.FromSuccess(); diff --git a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs index 04e221008..c5b79c5b9 100644 --- a/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/Preconditions/RequireUserPermissionAttribute.cs @@ -42,7 +42,7 @@ namespace Discord.Commands GuildPermission = null; } - public override Task CheckPermissions(CommandContext context, CommandInfo command, IDependencyMap map) + public override Task CheckPermissions(ICommandContext context, CommandInfo command, IDependencyMap map) { var guildUser = context.User as IGuildUser; diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index 3b14da263..eec43c230 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -12,7 +12,7 @@ namespace Discord.Commands.Builders private readonly List _aliases; public ModuleBuilder Module { get; } - internal Func Callback { get; set; } + internal Func Callback { get; set; } public string Name { get; set; } public string Summary { get; set; } @@ -34,7 +34,7 @@ namespace Discord.Commands.Builders _aliases = new List(); } //User-defined - internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func callback) + internal CommandBuilder(ModuleBuilder module, string primaryAlias, Func callback) : this(module) { Discord.Preconditions.NotNull(primaryAlias, nameof(primaryAlias)); diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 4e5b705f6..f15737e81 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -72,7 +72,7 @@ namespace Discord.Commands.Builders _preconditions.Add(precondition); return this; } - public ModuleBuilder AddCommand(string primaryAlias, Func callback, Action createFunc) + public ModuleBuilder AddCommand(string primaryAlias, Func callback, Action createFunc) { var builder = new CommandBuilder(this, primaryAlias, callback); createFunc(builder); diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 1333edd78..b7b02e4b2 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -10,7 +10,7 @@ namespace Discord.Commands { internal static class ModuleClassBuilder { - private static readonly TypeInfo _moduleTypeInfo = typeof(ModuleBase).GetTypeInfo(); + private static readonly TypeInfo _moduleTypeInfo = typeof(IModuleBase).GetTypeInfo(); public static IEnumerable Search(Assembly assembly) { @@ -155,12 +155,12 @@ namespace Discord.Commands }); } - var createInstance = ReflectionUtils.CreateBuilder(typeInfo, service); + var createInstance = ReflectionUtils.CreateBuilder(typeInfo, service); builder.Callback = (ctx, args, map) => { var instance = createInstance(map); - instance.Context = ctx; + instance.SetContext(ctx); try { return method.Invoke(instance, args) as Task ?? Task.Delay(0); diff --git a/src/Discord.Net.Commands/CommandContext.cs b/src/Discord.Net.Commands/CommandContext.cs index 50b41f02e..05bde56b1 100644 --- a/src/Discord.Net.Commands/CommandContext.cs +++ b/src/Discord.Net.Commands/CommandContext.cs @@ -1,6 +1,6 @@ namespace Discord.Commands { - public struct CommandContext + public class CommandContext : ICommandContext { public IDiscordClient Client { get; } public IGuild Guild { get; } @@ -9,15 +9,7 @@ public IUserMessage Message { get; } public bool IsPrivate => Channel is IPrivateChannel; - - public CommandContext(IDiscordClient client, IGuild guild, IMessageChannel channel, IUser user, IUserMessage msg) - { - Client = client; - Guild = guild; - Channel = channel; - User = user; - Message = msg; - } + public CommandContext(IDiscordClient client, IUserMessage msg) { Client = client; diff --git a/src/Discord.Net.Commands/CommandMatch.cs b/src/Discord.Net.Commands/CommandMatch.cs index 2e13e0f46..6e78b8509 100644 --- a/src/Discord.Net.Commands/CommandMatch.cs +++ b/src/Discord.Net.Commands/CommandMatch.cs @@ -14,13 +14,13 @@ namespace Discord.Commands Alias = alias; } - public Task CheckPreconditionsAsync(CommandContext context, IDependencyMap map = null) + public Task CheckPreconditionsAsync(ICommandContext context, IDependencyMap map = null) => Command.CheckPreconditionsAsync(context, map); - public Task ParseAsync(CommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null) + public Task ParseAsync(ICommandContext context, SearchResult searchResult, PreconditionResult? preconditionResult = null) => Command.ParseAsync(context, Alias.Length, searchResult, preconditionResult); - public Task ExecuteAsync(CommandContext context, IEnumerable argList, IEnumerable paramList, IDependencyMap map) + public Task ExecuteAsync(ICommandContext context, IEnumerable argList, IEnumerable paramList, IDependencyMap map) => Command.ExecuteAsync(context, argList, paramList, map); - public Task ExecuteAsync(CommandContext context, ParseResult parseResult, IDependencyMap map) + public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IDependencyMap map) => Command.ExecuteAsync(context, parseResult, map); } } diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 53ea1330f..5b4ba2480 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -13,7 +13,7 @@ namespace Discord.Commands QuotedParameter } - public static async Task ParseArgs(CommandInfo command, CommandContext context, string input, int startPos) + public static async Task ParseArgs(CommandInfo command, ICommandContext context, string input, int startPos) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 18c6c3426..0d27bd178 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -222,9 +222,9 @@ namespace Discord.Commands } //Execution - public SearchResult Search(CommandContext context, int argPos) + public SearchResult Search(ICommandContext context, int argPos) => Search(context, context.Message.Content.Substring(argPos)); - public SearchResult Search(CommandContext context, string input) + public SearchResult Search(ICommandContext context, string input) { string searchInput = _caseSensitive ? input : input.ToLowerInvariant(); var matches = _map.GetCommands(searchInput).OrderByDescending(x => x.Command.Priority).ToImmutableArray(); @@ -235,9 +235,9 @@ namespace Discord.Commands return SearchResult.FromError(CommandError.UnknownCommand, "Unknown command."); } - public Task ExecuteAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public Task ExecuteAsync(ICommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) => ExecuteAsync(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling); - public async Task ExecuteAsync(CommandContext context, string input, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) + public async Task ExecuteAsync(ICommandContext context, string input, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { dependencyMap = dependencyMap ?? DependencyMap.Empty; diff --git a/src/Discord.Net.Commands/IModuleBase.cs b/src/Discord.Net.Commands/IModuleBase.cs new file mode 100644 index 000000000..e65a059e3 --- /dev/null +++ b/src/Discord.Net.Commands/IModuleBase.cs @@ -0,0 +1,7 @@ +namespace Discord.Commands +{ + internal interface IModuleBase + { + void SetContext(ICommandContext context); + } +} diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index abe802d53..031d37581 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -17,7 +17,7 @@ namespace Discord.Commands private static readonly System.Reflection.MethodInfo _convertParamsMethod = typeof(CommandInfo).GetTypeInfo().GetDeclaredMethod(nameof(ConvertParamsList)); private static readonly ConcurrentDictionary, object>> _arrayConverters = new ConcurrentDictionary, object>>(); - private readonly Func _action; + private readonly Func _action; public ModuleInfo Module { get; } public string Name { get; } @@ -63,7 +63,7 @@ namespace Discord.Commands _action = builder.Callback; } - public async Task CheckPreconditionsAsync(CommandContext context, IDependencyMap map = null) + public async Task CheckPreconditionsAsync(ICommandContext context, IDependencyMap map = null) { if (map == null) map = DependencyMap.Empty; @@ -85,7 +85,7 @@ namespace Discord.Commands return PreconditionResult.FromSuccess(); } - public async Task ParseAsync(CommandContext context, int startIndex, SearchResult searchResult, PreconditionResult? preconditionResult = null) + public async Task ParseAsync(ICommandContext context, int startIndex, SearchResult searchResult, PreconditionResult? preconditionResult = null) { if (!searchResult.IsSuccess) return ParseResult.FromError(searchResult); @@ -96,7 +96,7 @@ namespace Discord.Commands return await CommandParser.ParseArgs(this, context, input, 0).ConfigureAwait(false); } - public Task ExecuteAsync(CommandContext context, ParseResult parseResult, IDependencyMap map) + public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IDependencyMap map) { if (!parseResult.IsSuccess) return Task.FromResult(ExecuteResult.FromError(parseResult)); @@ -119,7 +119,7 @@ namespace Discord.Commands return ExecuteAsync(context, argList, paramList, map); } - public async Task ExecuteAsync(CommandContext context, IEnumerable argList, IEnumerable paramList, IDependencyMap map) + public async Task ExecuteAsync(ICommandContext context, IEnumerable argList, IEnumerable paramList, IDependencyMap map) { if (map == null) map = DependencyMap.Empty; diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index f8a97647a..45ce3f0f4 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -41,7 +41,7 @@ namespace Discord.Commands _reader = builder.TypeReader; } - public async Task CheckPreconditionsAsync(CommandContext context, object[] args, IDependencyMap map = null) + public async Task CheckPreconditionsAsync(ICommandContext context, object[] args, IDependencyMap map = null) { if (map == null) map = DependencyMap.Empty; @@ -61,7 +61,7 @@ namespace Discord.Commands return PreconditionResult.FromSuccess(); } - public async Task Parse(CommandContext context, string input) + public async Task Parse(ICommandContext context, string input) { return await _reader.Read(context, input).ConfigureAwait(false); } diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index 96dedcb23..ccd96ed08 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -1,14 +1,27 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Discord.Commands { - public abstract class ModuleBase + public abstract class ModuleBase : ModuleBase { } + + public abstract class ModuleBase : IModuleBase + where T : class, ICommandContext { - public CommandContext Context { get; internal set; } + public T Context { get; private set; } protected virtual async Task ReplyAsync(string message, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) { return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); } + + //IModuleBase + void IModuleBase.SetContext(ICommandContext context) + { + var newValue = context as T; + if (newValue == null) + throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}"); + Context = newValue; + } } } diff --git a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs index e05c02abb..08821c62f 100644 --- a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs @@ -9,7 +9,7 @@ namespace Discord.Commands internal class ChannelTypeReader : TypeReader where T : class, IChannel { - public override async Task Read(CommandContext context, string input) + public override async Task Read(ICommandContext context, string input) { if (context.Guild != null) { diff --git a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs index 81870ecaf..7b2ff505a 100644 --- a/src/Discord.Net.Commands/Readers/EnumTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/EnumTypeReader.cs @@ -44,7 +44,7 @@ namespace Discord.Commands _enumsByValue = byValueBuilder.ToImmutable(); } - public override Task Read(CommandContext context, string input) + public override Task Read(ICommandContext context, string input) { T baseValue; object enumValue; diff --git a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs index 57bfc21cd..9baa1901a 100644 --- a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs @@ -6,7 +6,7 @@ namespace Discord.Commands internal class MessageTypeReader : TypeReader where T : class, IMessage { - public override async Task Read(CommandContext context, string input) + public override async Task Read(ICommandContext context, string input) { ulong id; diff --git a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs index 824154cd0..aa4c7c7a4 100644 --- a/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/PrimitiveTypeReader.cs @@ -21,7 +21,7 @@ namespace Discord.Commands _tryParse = PrimitiveParsers.Get(); } - public override Task Read(CommandContext context, string input) + public override Task Read(ICommandContext context, string input) { T value; if (_tryParse(input, out value)) diff --git a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs index 66b76b7e7..48544eeda 100644 --- a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs @@ -9,7 +9,7 @@ namespace Discord.Commands internal class RoleTypeReader : TypeReader where T : class, IRole { - public override Task Read(CommandContext context, string input) + public override Task Read(ICommandContext context, string input) { ulong id; diff --git a/src/Discord.Net.Commands/Readers/TypeReader.cs b/src/Discord.Net.Commands/Readers/TypeReader.cs index 23562cb16..d53491e92 100644 --- a/src/Discord.Net.Commands/Readers/TypeReader.cs +++ b/src/Discord.Net.Commands/Readers/TypeReader.cs @@ -4,6 +4,6 @@ namespace Discord.Commands { public abstract class TypeReader { - public abstract Task Read(CommandContext context, string input); + public abstract Task Read(ICommandContext context, string input); } } diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs index 82e8e9828..a5f92a277 100644 --- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs @@ -10,7 +10,7 @@ namespace Discord.Commands internal class UserTypeReader : TypeReader where T : class, IUser { - public override async Task Read(CommandContext context, string input) + public override async Task Read(ICommandContext context, string input) { var results = new Dictionary(); IReadOnlyCollection channelUsers = (await context.Channel.GetUsersAsync(CacheMode.CacheOnly).Flatten().ConfigureAwait(false)).ToArray(); //TODO: must be a better way? diff --git a/src/Discord.Net.Core/Commands/ICommandContext.cs b/src/Discord.Net.Core/Commands/ICommandContext.cs new file mode 100644 index 000000000..ac1424339 --- /dev/null +++ b/src/Discord.Net.Core/Commands/ICommandContext.cs @@ -0,0 +1,11 @@ +namespace Discord.Commands +{ + public interface ICommandContext + { + IDiscordClient Client { get; } + IGuild Guild { get; } + IMessageChannel Channel { get; } + IUser User { get; } + IUserMessage Message { get; } + } +} diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index d589dbd4b..ec4373263 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,4 +1,4 @@ - + A core Discord.Net library containing the REST client and models. 1.0.0-beta2 diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs similarity index 100% rename from src/Discord.Net.Rest/Entities/Channels/RestVirtualMessageChannel.cs rename to src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs diff --git a/src/Discord.Net.Rpc/Commands/RpcCommandContext.cs b/src/Discord.Net.Rpc/Commands/RpcCommandContext.cs new file mode 100644 index 000000000..80dfddbab --- /dev/null +++ b/src/Discord.Net.Rpc/Commands/RpcCommandContext.cs @@ -0,0 +1,29 @@ +using Discord.Rpc; + +namespace Discord.Commands +{ + public class RpcCommandContext : ICommandContext + { + public DiscordRpcClient Client { get; } + public IMessageChannel Channel { get; } + public RpcUser User { get; } + public RpcUserMessage Message { get; } + + public bool IsPrivate => Channel is IPrivateChannel; + + public RpcCommandContext(DiscordRpcClient client, RpcUserMessage msg) + { + Client = client; + Channel = msg.Channel; + User = msg.Author; + Message = msg; + } + + //ICommandContext + IDiscordClient ICommandContext.Client => Client; + IGuild ICommandContext.Guild => null; + IMessageChannel ICommandContext.Channel => Channel; + IUser ICommandContext.User => User; + IUserMessage ICommandContext.Message => Message; + } +} diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs index d0464487b..b85071f2a 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcMessage.cs @@ -33,7 +33,7 @@ namespace Discord.Rpc public DateTimeOffset Timestamp => DateTimeUtils.FromTicks(_timestampTicks); - internal RpcMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + internal RpcMessage(DiscordRpcClient discord, ulong id, RestVirtualMessageChannel channel, RpcUser author) : base(discord, id) { Channel = channel; @@ -62,7 +62,9 @@ namespace Discord.Rpc => MessageHelper.DeleteAsync(this, Discord, options); public override string ToString() => Content; - + + //IMessage + IMessageChannel IMessage.Channel => Channel; MessageType IMessage.Type => MessageType.Default; IUser IMessage.Author => Author; IReadOnlyCollection IMessage.Attachments => Attachments; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs index 734ef38bc..e8c918bc6 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcSystemMessage.cs @@ -9,7 +9,7 @@ namespace Discord.Rpc { public MessageType Type { get; private set; } - internal RpcSystemMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + internal RpcSystemMessage(DiscordRpcClient discord, ulong id, RestVirtualMessageChannel channel, RpcUser author) : base(discord, id, channel, author) { } diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index bf5ad6510..f7600bbcb 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -32,7 +32,7 @@ namespace Discord.Rpc public override IReadOnlyCollection Tags => _tags; public IReadOnlyDictionary Reactions => ImmutableDictionary.Create(); - internal RpcUserMessage(DiscordRpcClient discord, ulong id, IMessageChannel channel, RpcUser author) + internal RpcUserMessage(DiscordRpcClient discord, ulong id, RestVirtualMessageChannel channel, RpcUser author) : base(discord, id, channel, author) { } diff --git a/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs new file mode 100644 index 000000000..c8b0747e7 --- /dev/null +++ b/src/Discord.Net.WebSocket/Commands/SocketCommandContext.cs @@ -0,0 +1,31 @@ +using Discord.WebSocket; + +namespace Discord.Commands +{ + public class SocketCommandContext : ICommandContext + { + public DiscordSocketClient Client { get; } + public SocketGuild Guild { get; } + public ISocketMessageChannel Channel { get; } + public SocketUser User { get; } + public SocketUserMessage Message { get; } + + public bool IsPrivate => Channel is IPrivateChannel; + + public SocketCommandContext(DiscordSocketClient client, SocketUserMessage msg) + { + Client = client; + Guild = (msg.Channel as SocketGuildChannel)?.Guild; + Channel = msg.Channel; + User = msg.Author; + Message = msg; + } + + //ICommandContext + IDiscordClient ICommandContext.Client => Client; + IGuild ICommandContext.Guild => Guild; + IMessageChannel ICommandContext.Channel => Channel; + IUser ICommandContext.User => User; + IUserMessage ICommandContext.Message => Message; + } +} From 3ddc59c3863d311bf6e9bed95f880c48ebf4ee35 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 27 Dec 2016 14:52:22 -0400 Subject: [PATCH 113/263] Added NullableConverter, fixed Image serialization --- .../Net/Converters/DiscordContractResolver.cs | 69 ++++++++----------- .../Net/Converters/NullableConverter.cs | 50 ++++++++++++++ .../Net/Converters/NullableUInt64Converter.cs | 32 --------- 3 files changed, 77 insertions(+), 74 deletions(-) create mode 100644 src/Discord.Net.Core/Net/Converters/NullableConverter.cs delete mode 100644 src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs diff --git a/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs index 3357932d4..104b913da 100644 --- a/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs +++ b/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs @@ -22,26 +22,7 @@ namespace Discord.Net.Converters var propInfo = member as PropertyInfo; if (propInfo != null) { - JsonConverter converter; - Type type = propInfo.PropertyType; - Type genericType = type.IsConstructedGenericType ? type.GetGenericTypeDefinition() : null; - - if (genericType == typeof(Optional<>)) - { - var typeInput = propInfo.DeclaringType; - var innerTypeOutput = type.GenericTypeArguments[0]; - - var getter = typeof(Func<,>).MakeGenericType(typeInput, type); - var getterDelegate = propInfo.GetMethod.CreateDelegate(getter); - var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, innerTypeOutput); - var shouldSerializeDelegate = (Func)shouldSerialize.CreateDelegate(typeof(Func)); - property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); - - converter = MakeGenericConverter(propInfo, typeof(OptionalConverter<>), innerTypeOutput); - } - else - converter = GetConverter(propInfo, type); - + var converter = GetConverter(property, propInfo, propInfo.PropertyType, 0); if (converter != null) { property.Converter = converter; @@ -53,26 +34,38 @@ namespace Discord.Net.Converters return property; } - private static JsonConverter GetConverter(PropertyInfo propInfo, Type type, TypeInfo typeInfo = null, int depth = 0) + private static JsonConverter GetConverter(JsonProperty property, PropertyInfo propInfo, Type type, int depth) { if (type.IsArray) - return MakeGenericConverter(propInfo, typeof(ArrayConverter<>), type.GetElementType()); + return MakeGenericConverter(property, propInfo, typeof(ArrayConverter<>), type.GetElementType(), depth); if (type.IsConstructedGenericType) { Type genericType = type.GetGenericTypeDefinition(); - if (genericType == typeof(EntityOrId<>)) - return MakeGenericConverter(propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0]); - } + if (depth == 0 && genericType == typeof(Optional<>)) + { + var typeInput = propInfo.DeclaringType; + var innerTypeOutput = type.GenericTypeArguments[0]; - bool hasInt53 = propInfo.GetCustomAttribute() != null; + var getter = typeof(Func<,>).MakeGenericType(typeInput, type); + var getterDelegate = propInfo.GetMethod.CreateDelegate(getter); + var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, innerTypeOutput); + var shouldSerializeDelegate = (Func)shouldSerialize.CreateDelegate(typeof(Func)); + property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); + + return MakeGenericConverter(property, propInfo, typeof(OptionalConverter<>), innerTypeOutput, depth); + } + else if (genericType == typeof(Nullable<>)) + return MakeGenericConverter(property, propInfo, typeof(NullableConverter<>), type.GenericTypeArguments[0], depth); + else if (genericType == typeof(EntityOrId<>)) + return MakeGenericConverter(property, propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0], depth); + } //Primitives + bool hasInt53 = propInfo.GetCustomAttribute() != null; if (!hasInt53) { if (type == typeof(ulong)) return UInt64Converter.Instance; - if (type == typeof(ulong?)) - return NullableUInt64Converter.Instance; } //Enums @@ -82,12 +75,11 @@ namespace Discord.Net.Converters return UserStatusConverter.Instance; //Special - if (type == typeof(Image)) - return ImageConverter.Instance; - - if (typeInfo == null) typeInfo = type.GetTypeInfo(); + if (type == typeof(API.Image)) + return ImageConverter.Instance; //Entities + var typeInfo = type.GetTypeInfo(); if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity))) return UInt64EntityConverter.Instance; if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity))) @@ -101,18 +93,11 @@ namespace Discord.Net.Converters return (getter as Func>)((TOwner)owner).IsSpecified; } - private static JsonConverter MakeGenericConverter(PropertyInfo propInfo, Type converterType, Type innerType) + private static JsonConverter MakeGenericConverter(JsonProperty property, PropertyInfo propInfo, Type converterType, Type innerType, int depth) { var genericType = converterType.MakeGenericType(innerType).GetTypeInfo(); - //var instanceField = genericType.GetDeclaredField("Instance"); - //var converter = instanceField.GetValue(null) as JsonConverter; - //if (converter == null) - //{ - var innerConverter = GetConverter(propInfo, innerType); - var converter = genericType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; - //instanceField.SetValue(null, converter); - //} - return converter; + var innerConverter = GetConverter(property, propInfo, innerType, depth + 1); + return genericType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; } } } diff --git a/src/Discord.Net.Core/Net/Converters/NullableConverter.cs b/src/Discord.Net.Core/Net/Converters/NullableConverter.cs new file mode 100644 index 000000000..0b149e725 --- /dev/null +++ b/src/Discord.Net.Core/Net/Converters/NullableConverter.cs @@ -0,0 +1,50 @@ +using Newtonsoft.Json; +using System; + +namespace Discord.Net.Converters +{ + internal class NullableConverter : JsonConverter + where T : struct + { + private readonly JsonConverter _innerConverter; + + public override bool CanConvert(Type objectType) => true; + public override bool CanRead => true; + public override bool CanWrite => true; + + public NullableConverter(JsonConverter innerConverter) + { + _innerConverter = innerConverter; + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + object value = reader.Value; + if (value == null) + return null; + else + { + T obj; + if (_innerConverter != null) + obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); + else + obj = serializer.Deserialize(reader); + return obj; + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + writer.WriteNull(); + else + { + var nullable = (T?)value; + if (_innerConverter != null) + _innerConverter.WriteJson(writer, nullable.Value, serializer); + else + serializer.Serialize(writer, nullable.Value, typeof(T)); + } + } + } +} diff --git a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs b/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs deleted file mode 100644 index fa22da656..000000000 --- a/src/Discord.Net.Core/Net/Converters/NullableUInt64Converter.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Globalization; - -namespace Discord.Net.Converters -{ - internal class NullableUInt64Converter : JsonConverter - { - public static readonly NullableUInt64Converter Instance = new NullableUInt64Converter(); - - public override bool CanConvert(Type objectType) => true; - public override bool CanRead => true; - public override bool CanWrite => true; - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - object value = reader.Value; - if (value != null) - return ulong.Parse(value.ToString(), NumberStyles.None, CultureInfo.InvariantCulture); - else - return null; - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - if (value != null) - writer.WriteValue(((ulong?)value).Value.ToString(CultureInfo.InvariantCulture)); - else - writer.WriteNull(); - } - } -} From 83a5456011f64c37c634a0dce5435685421b652c Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 27 Dec 2016 15:04:49 -0400 Subject: [PATCH 114/263] Fixed RPC NullRef --- src/Discord.Net.Rpc/DiscordRpcApiClient.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs index 1348ccc7f..0377350fd 100644 --- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -59,7 +59,6 @@ namespace Discord.API private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); private readonly ConcurrentDictionary _requests; - private readonly RequestQueue _requestQueue; private readonly IWebSocketClient _webSocketClient; private readonly SemaphoreSlim _connectionLock; private readonly string _clientId; @@ -227,7 +226,7 @@ namespace Discord.API var requestTracker = new RpcRequest(options); _requests[guid] = requestTracker; - await _requestQueue.SendAsync(new WebSocketRequest(_webSocketClient, null, bytes, true, options)).ConfigureAwait(false); + await RequestQueue.SendAsync(new WebSocketRequest(_webSocketClient, null, bytes, true, options)).ConfigureAwait(false); await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false); return await requestTracker.Promise.Task.ConfigureAwait(false); } From d82d7e1584b9f106a3f9e72da747be9a1e441051 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 27 Dec 2016 16:50:30 -0400 Subject: [PATCH 115/263] Do not catch our thrown HttpException in RequestQueueBucket --- src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 332177de8..04508178f 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -117,6 +117,7 @@ namespace Discord.Net.Queue return response.Stream; } } + catch (HttpException) { throw; } //Pass through catch (TimeoutException) { #if DEBUG_LIMITS From 1169d3671c12423f58ca02e806ffb1903baefd9c Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 27 Dec 2016 17:50:32 -0400 Subject: [PATCH 116/263] Removed RetryMode.RetryErrors --- src/Discord.Net.Core/RetryMode.cs | 6 +++--- src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Core/RetryMode.cs b/src/Discord.Net.Core/RetryMode.cs index 9dccfc313..65ae75fc3 100644 --- a/src/Discord.Net.Core/RetryMode.cs +++ b/src/Discord.Net.Core/RetryMode.cs @@ -10,13 +10,13 @@ namespace Discord AlwaysFail = 0x0, /// Retry if a request timed out. RetryTimeouts = 0x1, - /// Retry if a request failed due to a network error. - RetryErrors = 0x2, + // /// Retry if a request failed due to a network error. + //RetryErrors = 0x2, /// Retry if a request failed due to a ratelimit. RetryRatelimit = 0x4, /// Retry if a request failed due to an HTTP error 502. Retry502 = 0x8, /// Continuously retry a request until it times out, its cancel token is triggered, or the server responds with a non-502 error. - AlwaysRetry = RetryTimeouts | RetryErrors | RetryRatelimit | Retry502, + AlwaysRetry = RetryTimeouts | /*RetryErrors |*/ RetryRatelimit | Retry502, } } diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 04508178f..5f23a626f 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -117,7 +117,7 @@ namespace Discord.Net.Queue return response.Stream; } } - catch (HttpException) { throw; } //Pass through + //catch (HttpException) { throw; } //Pass through catch (TimeoutException) { #if DEBUG_LIMITS @@ -129,7 +129,7 @@ namespace Discord.Net.Queue await Task.Delay(500); continue; //Retry } - catch (Exception) + /*catch (Exception) { #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Error"); @@ -139,7 +139,7 @@ namespace Discord.Net.Queue await Task.Delay(500); continue; //Retry - } + }*/ finally { UpdateRateLimit(id, request, info, lag, false); From 032c9e007c502e5d5cdd22205bd5902c61128562 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 28 Dec 2016 19:22:53 -0400 Subject: [PATCH 117/263] Do not add null aliases --- src/Discord.Net.Commands/Builders/CommandBuilder.cs | 5 +++-- src/Discord.Net.Commands/Builders/ModuleBuilder.cs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index eec43c230..27f991b16 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -74,8 +74,9 @@ namespace Discord.Commands.Builders { for (int i = 0; i < aliases.Length; i++) { - if (!_aliases.Contains(aliases[i])) - _aliases.Add(aliases[i]); + var alias = aliases[i] ?? ""; + if (!_aliases.Contains(alias)) + _aliases.Add(alias); } return this; } diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index f15737e81..45c0034f2 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -62,8 +62,9 @@ namespace Discord.Commands.Builders { for (int i = 0; i < aliases.Length; i++) { - if (!_aliases.Contains(aliases[i])) - _aliases.Add(aliases[i]); + var alias = aliases[i] ?? ""; + if (!_aliases.Contains(alias)) + _aliases.Add(alias); } return this; } From e3da623f39beb07cd04435bd20eaa48e4e84f2a8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 28 Dec 2016 19:22:57 -0400 Subject: [PATCH 118/263] Cleaned up alias generation --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 42 ++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index bff9d9a56..40a434592 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -30,44 +30,36 @@ namespace Discord.Commands Remarks = builder.Remarks; Parent = parent; - Aliases = BuildAliases(builder).ToImmutableArray(); + Aliases = BuildAliases(builder, service).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)).ToImmutableArray(); Preconditions = BuildPreconditions(builder).ToImmutableArray(); Submodules = BuildSubmodules(builder, service).ToImmutableArray(); } - private static IEnumerable BuildAliases(ModuleBuilder builder) + private static IEnumerable BuildAliases(ModuleBuilder builder, CommandService service) { - IEnumerable result = null; + var result = builder.Aliases.ToList(); + var builderStack = new Stack(); - Stack builderStack = new Stack(); - builderStack.Push(builder); - - ModuleBuilder parent = builder.Parent; - while (parent != null) - { + var parent = builder; + while ((parent = parent.Parent) != null) builderStack.Push(parent); - parent = parent.Parent; - } - while (builderStack.Count() > 0) + while (builderStack.Count > 0) { - ModuleBuilder level = builderStack.Pop(); //get the topmost builder - if (result == null) + var level = builderStack.Pop(); + result = result.Permutate(level.Aliases, (first, second) => { - if (level.Aliases.Count > 0) - result = level.Aliases.ToList(); //create a shallow copy so we don't overwrite the builder unexpectedly - } - else if (result.Count() > level.Aliases.Count) - result = result.Permutate(level.Aliases, (first, second) => first + " " + second); - else - result = level.Aliases.Permutate(result, (second, first) => first + " " + second); + if (first == "") + return second; + else if (second == "") + return first; + else + return first + service._separatorChar + second; + }).ToList(); } - if (result == null) //there were no aliases; default to an empty string alias - result = new List { "" }; - return result; } @@ -76,9 +68,7 @@ namespace Discord.Commands var result = new List(); foreach (var submodule in parent.Modules) - { result.Add(submodule.Build(service, this)); - } return result; } From c9a12cde10ddd91dc2fbd9eb4aca99b0d05147f9 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 29 Dec 2016 20:57:07 -0400 Subject: [PATCH 119/263] Added default alias when GroupAttribute isn't provided --- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index b7b02e4b2..1663b3dba 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -100,6 +100,9 @@ namespace Discord.Commands builder.AddPrecondition(attribute as PreconditionAttribute); } + //Check for unspecified info + if (builder.Aliases.Count == 0) + builder.AddAliases(""); if (builder.Name == null) builder.Name = typeInfo.Name; From 2877653a0940b6669f2636172e950ccd23f0b434 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 00:41:29 -0400 Subject: [PATCH 120/263] Readded outgoing audio --- src/Discord.Net.Core/Audio/IAudioClient.cs | 28 ++++++ .../Audio/AudioClient.cs | 38 +++++--- .../Audio/Opus/OpusEncoder.cs | 2 +- .../Audio/Streams/OpusEncodeStream.cs | 4 +- .../Audio/Streams/RTPWriteStream.cs | 8 +- .../Audio/Targets/BufferedAudioTarget.cs | 79 +++++++++++++++ .../Audio/Targets/DirectAudioTarget.cs | 16 ++++ .../Audio/Targets/IAudioTarget.cs | 9 ++ .../Discord.Net.WebSocket.csproj | 2 +- .../DiscordSocketClient.cs | 4 +- .../Entities/Channels/ISocketAudioChannel.cs | 6 +- .../Entities/Channels/SocketGroupChannel.cs | 8 +- .../Entities/Channels/SocketVoiceChannel.cs | 14 ++- .../Entities/Guilds/SocketGuild.cs | 95 ++++++++++++------- .../Net/DefaultUdpSocket.cs | 3 +- 15 files changed, 254 insertions(+), 62 deletions(-) create mode 100644 src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 2fbb4a450..746515fa7 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -17,7 +17,35 @@ namespace Discord.Audio Task DisconnectAsync(); + /// + /// Creates a new outgoing stream accepting Opus-encoded data. + /// + /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. + /// The size of the internal buffer used for encryption. + /// Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); + /// + /// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer. + /// + /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. + /// The size of the internal buffer used for encryption. + /// + Stream CreateDirectOpusStream(int samplesPerFrame, int bufferSize = 4000); + /// + /// Creates a new outgoing stream accepting PCM (raw) data. + /// + /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. + /// + /// The size of the internal buffer used for encoding and encryption. + /// Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000); + /// + /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. + /// + /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. + /// + /// The size of the internal buffer used for encoding and encryption. + /// + Stream CreateDirectPCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 7443c569a..2d11baaec 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -39,7 +39,7 @@ namespace Discord.Audio private readonly JsonSerializer _serializer; private TaskCompletionSource _connectTask; - private CancellationTokenSource _cancelToken; + private CancellationTokenSource _cancelTokenSource; private Task _heartbeatTask; private long _heartbeatTime; private string _url; @@ -110,7 +110,7 @@ namespace Discord.Audio { _url = url; _connectTask = new TaskCompletionSource(); - _cancelToken = new CancellationTokenSource(); + _cancelTokenSource = new CancellationTokenSource(); await ApiClient.ConnectAsync("wss://" + url).ConfigureAwait(false); await ApiClient.SendIdentityAsync(userId, sessionId, token).ConfigureAwait(false); @@ -152,7 +152,7 @@ namespace Discord.Audio await _audioLogger.InfoAsync("Disconnecting").ConfigureAwait(false); //Signal tasks to complete - try { _cancelToken.Cancel(); } catch { } + try { _cancelTokenSource.Cancel(); } catch { } //Disconnect from server await ApiClient.DisconnectAsync().ConfigureAwait(false); @@ -169,19 +169,35 @@ namespace Discord.Audio await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); } - public void Send(byte[] data, int count) + public Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000) { - //TODO: Queue these? - ApiClient.SendAsync(data, count).ConfigureAwait(false); + CheckSamplesPerFrame(samplesPerFrame); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); + return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); } - - public Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000) + public Stream CreateDirectOpusStream(int samplesPerFrame, int bufferSize = 4000) { - return new RTPWriteStream(this, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); + CheckSamplesPerFrame(samplesPerFrame); + var target = new DirectAudioTarget(ApiClient); + return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); } public Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000) { - return new OpusEncodeStream(this, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); + CheckSamplesPerFrame(samplesPerFrame); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); + return new OpusEncodeStream(target, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); + } + public Stream CreateDirectPCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000) + { + CheckSamplesPerFrame(samplesPerFrame); + var target = new DirectAudioTarget(ApiClient); + return new OpusEncodeStream(target, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); + } + private void CheckSamplesPerFrame(int samplesPerFrame) + { + if (samplesPerFrame != 120 && samplesPerFrame != 240 && samplesPerFrame != 480 && + samplesPerFrame != 960 && samplesPerFrame != 1920 && samplesPerFrame != 2880) + throw new ArgumentException("Value must be 120, 240, 480, 960, 1920 or 2880", nameof(samplesPerFrame)); } private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) @@ -201,7 +217,7 @@ namespace Discord.Audio throw new InvalidOperationException($"Discord does not support {DiscordVoiceAPIClient.Mode}"); _heartbeatTime = 0; - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token); + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelTokenSource.Token); ApiClient.SetUdpEndpoint(_url, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs index c1eb3843d..ca044bd69 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs @@ -52,7 +52,7 @@ namespace Discord.Audio throw new Exception($"Opus Error: {(OpusError)result}"); } - /// Gets or sets whether Forward Error Correction is enabled. + /// Gets or sets the encoder's bitrate. public void SetBitrate(int value) { if (value < 1 || value > DiscordVoiceAPIClient.MaxBitrate) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index 3806cc8bb..f86901ef1 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -7,8 +7,8 @@ private readonly OpusEncoder _encoder; - internal OpusEncodeStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int? bitrate = null, int bufferSize = 4000) - : base(audioClient, secretKey, samplesPerFrame, ssrc, bufferSize) + internal OpusEncodeStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc, int? bitrate = null, int bufferSize = 4000) + : base(target, secretKey, samplesPerFrame, ssrc, bufferSize) { _encoder = new OpusEncoder(SampleRate, Channels); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index db755c877..5ea0d2473 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -5,7 +5,7 @@ namespace Discord.Audio { internal class RTPWriteStream : Stream { - private readonly AudioClient _audioClient; + private readonly IAudioTarget _target; private readonly byte[] _nonce, _secretKey; private int _samplesPerFrame; private uint _ssrc, _timestamp = 0; @@ -16,9 +16,9 @@ namespace Discord.Audio public override bool CanSeek => false; public override bool CanWrite => true; - internal RTPWriteStream(AudioClient audioClient, byte[] secretKey, int samplesPerFrame, uint ssrc, int bufferSize = 4000) + internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc, int bufferSize = 4000) { - _audioClient = audioClient; + _target = target; _secretKey = secretKey; _samplesPerFrame = samplesPerFrame; _ssrc = ssrc; @@ -48,7 +48,7 @@ namespace Discord.Audio count = SecretBox.Encrypt(buffer, offset, count, _buffer, 12, _nonce, _secretKey); Buffer.BlockCopy(_nonce, 0, _buffer, 0, 12); //Copy the RTP header from nonce to buffer - _audioClient.Send(_buffer, count + 12); + _target.SendAsync(_buffer, count + 12).GetAwaiter().GetResult(); } public override void Flush() { } diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs new file mode 100644 index 000000000..e965d4aca --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio +{ + internal class BufferedAudioTarget : IAudioTarget, IDisposable + { + private static readonly byte[] _silencePacket = new byte[] { 0xF8, 0xFF, 0xFE }; + + private double _ticksPerFrame; + private Task _task; + private DiscordVoiceAPIClient _client; + private CancellationTokenSource _cancelTokenSource; + private ConcurrentQueue _queue; + + internal BufferedAudioTarget(DiscordVoiceAPIClient client, int samplesPerFrame, CancellationToken cancelToken) + { + _client = client; + double milliseconds = samplesPerFrame / 48.0; + double ticksPerFrame = Stopwatch.Frequency / 1000.0 * milliseconds; + + _cancelTokenSource = new CancellationTokenSource(); + cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; + _queue = new ConcurrentQueue(); //TODO: We need a better queue + + _task = Run(ticksPerFrame, cancelToken); + } + + private Task Run(double ticksPerFrame, CancellationToken cancelToken) + { + return Task.Run(async () => + { + var stopwatch = Stopwatch.StartNew(); + long lastTick = stopwatch.ElapsedTicks; + double ticksPerMilli = Stopwatch.Frequency / 1000.0; + while (!cancelToken.IsCancellationRequested) + { + long thisTick = stopwatch.ElapsedTicks; + double remaining = ticksPerFrame - (thisTick - lastTick); + if (remaining <= 0) + { + byte[] buffer; + if (_queue.TryDequeue(out buffer)) + await _client.SendAsync(buffer, buffer.Length).ConfigureAwait(false); + else + await _client.SendAsync(_silencePacket, _silencePacket.Length).ConfigureAwait(false); + lastTick = thisTick; + } + else if (remaining > 1) + { + int millis = (int)Math.Floor(remaining / ticksPerMilli); + await Task.Delay(millis).ConfigureAwait(false); + } + } + }); + } + + public Task SendAsync(byte[] buffer, int count) + { + byte[] newBuffer = new byte[count]; + Buffer.BlockCopy(buffer, 0, newBuffer, 0, count); + _queue.Enqueue(newBuffer); + return Task.Delay(0); + } + + protected void Dispose(bool disposing) + { + if (disposing) + _cancelTokenSource.Cancel(); + } + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs new file mode 100644 index 000000000..08cdfcfe6 --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Discord.Audio +{ + internal class DirectAudioTarget : IAudioTarget + { + private readonly DiscordVoiceAPIClient _client; + public DirectAudioTarget(DiscordVoiceAPIClient client) + { + _client = client; + } + + public Task SendAsync(byte[] buffer, int count) + => _client.SendAsync(buffer, count); + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs new file mode 100644 index 000000000..51b19a862 --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Discord.Audio +{ + internal interface IAudioTarget + { + Task SendAsync(byte[] buffer, int count); + } +} diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index df16ad855..593d9e75e 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,4 +1,4 @@ - + A core Discord.Net library containing the WebSocket client and models. 1.0.0-beta2 diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 647847498..c0bbb750b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1563,10 +1563,10 @@ namespace Discord.WebSocket { before = guild.GetVoiceState(data.UserId)?.Clone() ?? SocketVoiceState.Default; after = guild.AddOrUpdateVoiceState(State, data); - if (data.UserId == CurrentUser.Id) + /*if (data.UserId == CurrentUser.Id) { var _ = guild.FinishJoinAudioChannel().ConfigureAwait(false); - } + }*/ } else { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs index 7056a4df5..7b9bf07f0 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketAudioChannel.cs @@ -1,6 +1,10 @@ -namespace Discord.WebSocket +using Discord.Audio; +using System.Threading.Tasks; + +namespace Discord.WebSocket { public interface ISocketAudioChannel : IAudioChannel { + Task ConnectAsync(); } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 980b6dc2a..706d972df 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -1,4 +1,5 @@ -using Discord.Rest; +using Discord.Audio; +using Discord.Rest; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -64,6 +65,11 @@ namespace Discord.WebSocket public Task LeaveAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); + public Task ConnectAsync() + { + throw new NotSupportedException("Voice is not yet supported for group channels."); + } + //Messages public SocketMessage GetCachedMessage(ulong id) => _messages?.Get(id); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index a2c1e217b..072ccc787 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -41,6 +41,17 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); + public async Task ConnectAsync() + { + var audioMode = Discord.AudioMode; + if (audioMode == AudioMode.Disabled) + throw new InvalidOperationException($"Audio is not enabled on this client, {nameof(DiscordSocketConfig.AudioMode)} in {nameof(DiscordSocketConfig)} must be set."); + + return await Guild.ConnectAudioAsync(Id, + (audioMode & AudioMode.Incoming) == 0, + (audioMode & AudioMode.Outgoing) == 0).ConfigureAwait(false); + } + public override SocketGuildUser GetUser(ulong id) { var user = Guild.GetUser(id); @@ -52,9 +63,6 @@ namespace Discord.WebSocket private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel; - //IVoiceChannel - Task IVoiceChannel.ConnectAsync() { throw new NotSupportedException(); } - //IGuildChannel Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetUser(id)); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 7ed5bf8f9..c9da2226a 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -421,34 +421,64 @@ namespace Discord.WebSocket } //Audio - public async Task DisconnectAudioAsync(AudioClient client = null) + internal async Task ConnectAudioAsync(ulong channelId, bool selfDeaf, bool selfMute) { + selfDeaf = false; + selfMute = false; + + TaskCompletionSource promise; + await _audioLock.WaitAsync().ConfigureAwait(false); try { - await DisconnectAudioInternalAsync(client).ConfigureAwait(false); + await DisconnectAudioInternalAsync().ConfigureAwait(false); + promise = new TaskCompletionSource(); + _audioConnectPromise = promise; + await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false); + } + catch (Exception) + { + await DisconnectAudioInternalAsync().ConfigureAwait(false); + throw; } finally { _audioLock.Release(); } + + try + { + var timeoutTask = Task.Delay(15000); + if (await Task.WhenAny(promise.Task, timeoutTask) == timeoutTask) + throw new TimeoutException(); + return await promise.Task.ConfigureAwait(false); + } + catch (Exception) + { + await DisconnectAudioAsync().ConfigureAwait(false); + throw; + } } - private async Task DisconnectAudioInternalAsync(AudioClient client = null) + + internal async Task DisconnectAudioAsync() { - var oldClient = AudioClient; - if (oldClient != null) + await _audioLock.WaitAsync().ConfigureAwait(false); + try { - if (client == null || oldClient == client) - { - _audioConnectPromise?.TrySetCanceledAsync(); //Cancel any previous audio connection - _audioConnectPromise = null; - } - if (oldClient == client) - { - AudioClient = null; - await oldClient.DisconnectAsync().ConfigureAwait(false); - } + await DisconnectAudioInternalAsync().ConfigureAwait(false); } + finally + { + _audioLock.Release(); + } + } + private async Task DisconnectAudioInternalAsync() + { + _audioConnectPromise?.TrySetCanceledAsync(); //Cancel any previous audio connection + _audioConnectPromise = null; + if (AudioClient != null) + await AudioClient.DisconnectAsync().ConfigureAwait(false); + AudioClient = null; } internal async Task FinishConnectAudio(int id, string url, string token) { @@ -462,6 +492,14 @@ namespace Discord.WebSocket var audioClient = new AudioClient(this, id); audioClient.Disconnected += async ex => { + //If the initial connection hasn't been made yet, reconnecting will lead to deadlocks + if (!_audioConnectPromise.Task.IsCompleted) + { + try { audioClient.Dispose(); } catch { } + AudioClient = null; + return; + } + await _audioLock.WaitAsync().ConfigureAwait(false); try { @@ -476,14 +514,14 @@ namespace Discord.WebSocket { var voiceChannelId = voiceState2.Value.VoiceChannel?.Id; if (voiceChannelId != null) + { await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, voiceChannelId, voiceState2.Value.IsSelfDeafened, voiceState2.Value.IsSelfMuted); + return; + } } } - else - { - try { AudioClient.Dispose(); } catch { } - AudioClient = null; - } + try { audioClient.Dispose(); } catch { } + AudioClient = null; } } finally @@ -498,25 +536,12 @@ namespace Discord.WebSocket } catch (OperationCanceledException) { - await DisconnectAudioAsync().ConfigureAwait(false); + await DisconnectAudioInternalAsync().ConfigureAwait(false); } catch (Exception e) { await _audioConnectPromise.SetExceptionAsync(e).ConfigureAwait(false); - await DisconnectAudioAsync().ConfigureAwait(false); - } - finally - { - _audioLock.Release(); - } - } - internal async Task FinishJoinAudioChannel() - { - await _audioLock.WaitAsync().ConfigureAwait(false); - try - { - if (AudioClient != null) - await _audioConnectPromise.TrySetResultAsync(AudioClient).ConfigureAwait(false); + await DisconnectAudioInternalAsync().ConfigureAwait(false); } finally { diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs index 20620a3be..eb184e345 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -22,6 +22,7 @@ namespace Discord.Net.Udp public DefaultUdpSocket() { _lock = new SemaphoreSlim(1, 1); + _cancelTokenSource = new CancellationTokenSource(); } private void Dispose(bool disposing) { @@ -57,7 +58,7 @@ namespace Discord.Net.Udp _cancelTokenSource = new CancellationTokenSource(); _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; - _udp = new UdpClient(); + _udp = new UdpClient(0); _task = RunAsync(_cancelToken); } From c38a786039c6fd979e9f75885ed0735da41059bc Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 05:59:43 -0400 Subject: [PATCH 121/263] Removed unused parameter --- src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index e965d4aca..19bdee245 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -9,8 +9,7 @@ namespace Discord.Audio internal class BufferedAudioTarget : IAudioTarget, IDisposable { private static readonly byte[] _silencePacket = new byte[] { 0xF8, 0xFF, 0xFE }; - - private double _ticksPerFrame; + private Task _task; private DiscordVoiceAPIClient _client; private CancellationTokenSource _cancelTokenSource; From 3b72d489506501fedad9cb0b2df9cb225c851d12 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 16:02:01 -0400 Subject: [PATCH 122/263] Added support for partial PCM frames --- src/Discord.Net.Core/Audio/IAudioClient.cs | 12 ++---- .../Audio/AudioClient.cs | 16 +++---- .../Audio/Streams/OpusEncodeStream.cs | 43 +++++++++++++++---- .../Audio/Streams/RTPWriteStream.cs | 4 +- .../Audio/Targets/BufferedAudioTarget.cs | 2 +- 5 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 746515fa7..c8e8de747 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -21,31 +21,27 @@ namespace Discord.Audio /// Creates a new outgoing stream accepting Opus-encoded data. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. - /// The size of the internal buffer used for encryption. /// - Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000); + Stream CreateOpusStream(int samplesPerFrame); /// /// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. - /// The size of the internal buffer used for encryption. /// - Stream CreateDirectOpusStream(int samplesPerFrame, int bufferSize = 4000); + Stream CreateDirectOpusStream(int samplesPerFrame); /// /// Creates a new outgoing stream accepting PCM (raw) data. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// - /// The size of the internal buffer used for encoding and encryption. /// - Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000); + Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); /// /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// - /// The size of the internal buffer used for encoding and encryption. /// - Stream CreateDirectPCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000); + Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 2d11baaec..0f5079caa 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -169,29 +169,29 @@ namespace Discord.Audio await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); } - public Stream CreateOpusStream(int samplesPerFrame, int bufferSize = 4000) + public Stream CreateOpusStream(int samplesPerFrame) { CheckSamplesPerFrame(samplesPerFrame); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); - return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); + return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } - public Stream CreateDirectOpusStream(int samplesPerFrame, int bufferSize = 4000) + public Stream CreateDirectOpusStream(int samplesPerFrame) { CheckSamplesPerFrame(samplesPerFrame); var target = new DirectAudioTarget(ApiClient); - return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc, bufferSize = 4000); + return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } - public Stream CreatePCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000) + public Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null) { CheckSamplesPerFrame(samplesPerFrame); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); - return new OpusEncodeStream(target, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); + return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); } - public Stream CreateDirectPCMStream(int samplesPerFrame, int? bitrate = null, int bufferSize = 4000) + public Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null) { CheckSamplesPerFrame(samplesPerFrame); var target = new DirectAudioTarget(ApiClient); - return new OpusEncodeStream(target, _secretKey, samplesPerFrame, _ssrc, bitrate, bufferSize); + return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); } private void CheckSamplesPerFrame(int samplesPerFrame) { diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index f86901ef1..d207e3cf7 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -1,16 +1,22 @@ -namespace Discord.Audio +using System; + +namespace Discord.Audio { internal class OpusEncodeStream : RTPWriteStream { - public int SampleRate = 48000; - public int Channels = 2; - + public const int SampleRate = 48000; + private int _frameSize; + private byte[] _partialFrameBuffer; + private int _partialFramePos; + private readonly OpusEncoder _encoder; - internal OpusEncodeStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc, int? bitrate = null, int bufferSize = 4000) - : base(target, secretKey, samplesPerFrame, ssrc, bufferSize) + internal OpusEncodeStream(IAudioTarget target, byte[] secretKey, int channels, int samplesPerFrame, uint ssrc, int? bitrate = null) + : base(target, secretKey, samplesPerFrame, ssrc) { - _encoder = new OpusEncoder(SampleRate, Channels); + _encoder = new OpusEncoder(SampleRate, channels); + _frameSize = samplesPerFrame * channels * 2; + _partialFrameBuffer = new byte[_frameSize]; _encoder.SetForwardErrorCorrection(true); if (bitrate != null) @@ -19,8 +25,27 @@ public override void Write(byte[] buffer, int offset, int count) { - count = _encoder.EncodeFrame(buffer, offset, count, _buffer, 0); - base.Write(_buffer, 0, count); + //Assume threadsafe + while (count > 0) + { + if (_partialFramePos + count >= _frameSize) + { + int partialSize = _frameSize - _partialFramePos; + Buffer.BlockCopy(buffer, offset, _partialFrameBuffer, _partialFramePos, partialSize); + offset += partialSize; + count -= partialSize; + _partialFramePos = 0; + + int encFrameSize = _encoder.EncodeFrame(_partialFrameBuffer, 0, _frameSize, _buffer, 0); + base.Write(_buffer, 0, encFrameSize); + } + else + { + Buffer.BlockCopy(buffer, offset, _partialFrameBuffer, _partialFramePos, count); + _partialFramePos += count; + break; + } + } } protected override void Dispose(bool disposing) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 5ea0d2473..217555052 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -16,13 +16,13 @@ namespace Discord.Audio public override bool CanSeek => false; public override bool CanWrite => true; - internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc, int bufferSize = 4000) + internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc) { _target = target; _secretKey = secretKey; _samplesPerFrame = samplesPerFrame; _ssrc = ssrc; - _buffer = new byte[bufferSize]; + _buffer = new byte[4000]; _nonce = new byte[24]; _nonce[0] = 0x80; _nonce[1] = 0x78; diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index 19bdee245..848a77131 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -75,4 +75,4 @@ namespace Discord.Audio Dispose(true); } } -} +} \ No newline at end of file From beb2acb40ca51d680e5d3442b61d6c1e088add16 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 16:17:38 -0400 Subject: [PATCH 123/263] Fixed deadlock during connection --- .../Entities/Guilds/SocketGuild.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index c9da2226a..428171212 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -479,6 +479,7 @@ namespace Discord.WebSocket if (AudioClient != null) await AudioClient.DisconnectAsync().ConfigureAwait(false); AudioClient = null; + await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, null, false, false).ConfigureAwait(false); } internal async Task FinishConnectAudio(int id, string url, string token) { @@ -490,17 +491,23 @@ namespace Discord.WebSocket if (AudioClient == null) { var audioClient = new AudioClient(this, id); + var promise = _audioConnectPromise; audioClient.Disconnected += async ex => { //If the initial connection hasn't been made yet, reconnecting will lead to deadlocks - if (!_audioConnectPromise.Task.IsCompleted) + if (!promise.Task.IsCompleted) { try { audioClient.Dispose(); } catch { } AudioClient = null; + if (ex != null) + await promise.TrySetExceptionAsync(ex); + else + await promise.TrySetCanceledAsync(); return; } - await _audioLock.WaitAsync().ConfigureAwait(false); + //TODO: Implement reconnect + /*await _audioLock.WaitAsync().ConfigureAwait(false); try { if (AudioClient == audioClient) //Only reconnect if we're still assigned as this guild's audio client @@ -527,7 +534,7 @@ namespace Discord.WebSocket finally { _audioLock.Release(); - } + }*/ }; AudioClient = audioClient; } From f4469a76be446e9c82a1e9fb595c0234103277e6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 17:02:25 -0400 Subject: [PATCH 124/263] Added Flush support for audio streams --- .../Audio/AudioClient.cs | 1 + .../Audio/Streams/OpusEncodeStream.cs | 24 ++++++++++++++++++- .../Audio/Streams/RTPWriteStream.cs | 17 +++++++++++-- .../Audio/Targets/BufferedAudioTarget.cs | 10 ++++++++ .../Audio/Targets/DirectAudioTarget.cs | 3 +++ .../Audio/Targets/IAudioTarget.cs | 1 + 6 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 0f5079caa..0f2f50309 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -314,6 +314,7 @@ namespace Discord.Audio internal void Dispose(bool disposing) { + DisconnectInternalAsync(null).GetAwaiter().GetResult(); if (!_isDisposed) _isDisposed = true; ApiClient.Dispose(); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index d207e3cf7..a7c6e45a0 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -1,4 +1,6 @@ using System; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio { @@ -24,6 +26,10 @@ namespace Discord.Audio } public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { //Assume threadsafe while (count > 0) @@ -37,7 +43,7 @@ namespace Discord.Audio _partialFramePos = 0; int encFrameSize = _encoder.EncodeFrame(_partialFrameBuffer, 0, _frameSize, _buffer, 0); - base.Write(_buffer, 0, encFrameSize); + await base.WriteAsync(_buffer, 0, encFrameSize, cancellationToken).ConfigureAwait(false); } else { @@ -48,6 +54,22 @@ namespace Discord.Audio } } + public override void Flush() + { + FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task FlushAsync(CancellationToken cancellationToken) + { + try + { + int encFrameSize = _encoder.EncodeFrame(_partialFrameBuffer, 0, _partialFramePos, _buffer, 0); + base.Write(_buffer, 0, encFrameSize); + } + catch (Exception) { } //Incomplete frame + _partialFramePos = 0; + await base.FlushAsync(cancellationToken).ConfigureAwait(false); + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 217555052..300cd194c 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -1,5 +1,7 @@ using System; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio { @@ -33,6 +35,10 @@ namespace Discord.Audio } public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { unchecked { @@ -48,10 +54,17 @@ namespace Discord.Audio count = SecretBox.Encrypt(buffer, offset, count, _buffer, 12, _nonce, _secretKey); Buffer.BlockCopy(_nonce, 0, _buffer, 0, 12); //Copy the RTP header from nonce to buffer - _target.SendAsync(_buffer, count + 12).GetAwaiter().GetResult(); + await _target.SendAsync(_buffer, count + 12).ConfigureAwait(false); } - public override void Flush() { } + public override void Flush() + { + FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task FlushAsync(CancellationToken cancellationToken) + { + await _target.FlushAsync().ConfigureAwait(false); + } public override long Length { get { throw new NotSupportedException(); } } public override long Position diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index 848a77131..e227a975c 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -65,6 +65,16 @@ namespace Discord.Audio return Task.Delay(0); } + public async Task FlushAsync() + { + while (true) + { + if (_queue.Count == 0) + return; + await Task.Delay(250).ConfigureAwait(false); + } + } + protected void Dispose(bool disposing) { if (disposing) diff --git a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs index 08cdfcfe6..c5b0983d0 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs @@ -12,5 +12,8 @@ namespace Discord.Audio public Task SendAsync(byte[] buffer, int count) => _client.SendAsync(buffer, count); + + public Task FlushAsync() + => Task.Delay(0); } } diff --git a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs index 51b19a862..8c4384550 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs @@ -5,5 +5,6 @@ namespace Discord.Audio internal interface IAudioTarget { Task SendAsync(byte[] buffer, int count); + Task FlushAsync(); } } From 082ddc616c91a5f79abc43bea9366c46c2a52b82 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 17:06:15 -0400 Subject: [PATCH 125/263] Added IDisposable to IAudioClient --- src/Discord.Net.Core/Audio/IAudioClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index c8e8de747..5e7aa2f6e 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; namespace Discord.Audio { - public interface IAudioClient + public interface IAudioClient : IDisposable { event Func Connected; event Func Disconnected; From d6dc3a4e4ba8535cabc99705b6d26bc38dc2214b Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 17:20:39 -0400 Subject: [PATCH 126/263] Added IDisposable to RTPWriteStream. Flush on Dispose. --- src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 300cd194c..8317d578b 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -34,6 +34,12 @@ namespace Discord.Audio _nonce[11] = (byte)(_ssrc >> 0); } + protected override void Dispose(bool disposing) + { + Flush(); + base.Dispose(disposing); + } + public override void Write(byte[] buffer, int offset, int count) { WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); From 73461d60c117299f375efbe2f8de84ab3616f6b4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 17:20:59 -0400 Subject: [PATCH 127/263] Changed bitrate to bits/sec --- src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs | 2 +- src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs index ca044bd69..2cb3949a9 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs @@ -58,7 +58,7 @@ namespace Discord.Audio if (value < 1 || value > DiscordVoiceAPIClient.MaxBitrate) throw new ArgumentOutOfRangeException(nameof(value)); - var result = EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value * 1000); + var result = EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value); if (result < 0) throw new Exception($"Opus Error: {(OpusError)result}"); } diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index adbffc780..1773214ad 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -18,7 +18,7 @@ namespace Discord.Audio { public class DiscordVoiceAPIClient { - public const int MaxBitrate = 128; + public const int MaxBitrate = 128 * 1024; public const string Mode = "xsalsa20_poly1305"; public event Func SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } From 9cca612468ae19541b372fc519ad4bd57ed848a0 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 17:41:56 -0400 Subject: [PATCH 128/263] Improved BufferedAudioTarget loop --- .../Audio/Targets/BufferedAudioTarget.cs | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index e227a975c..32b7c2c4d 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -18,8 +18,7 @@ namespace Discord.Audio internal BufferedAudioTarget(DiscordVoiceAPIClient client, int samplesPerFrame, CancellationToken cancelToken) { _client = client; - double milliseconds = samplesPerFrame / 48.0; - double ticksPerFrame = Stopwatch.Frequency / 1000.0 * milliseconds; + long ticksPerFrame = samplesPerFrame / 48; _cancelTokenSource = new CancellationTokenSource(); cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; @@ -28,31 +27,26 @@ namespace Discord.Audio _task = Run(ticksPerFrame, cancelToken); } - private Task Run(double ticksPerFrame, CancellationToken cancelToken) + private Task Run(long ticksPerFrame, CancellationToken cancelToken) { return Task.Run(async () => { - var stopwatch = Stopwatch.StartNew(); - long lastTick = stopwatch.ElapsedTicks; - double ticksPerMilli = Stopwatch.Frequency / 1000.0; + long nextTick = Environment.TickCount; while (!cancelToken.IsCancellationRequested) { - long thisTick = stopwatch.ElapsedTicks; - double remaining = ticksPerFrame - (thisTick - lastTick); - if (remaining <= 0) + long tick = Environment.TickCount; + long dist = nextTick - tick; + if (dist <= 0) { byte[] buffer; if (_queue.TryDequeue(out buffer)) await _client.SendAsync(buffer, buffer.Length).ConfigureAwait(false); else await _client.SendAsync(_silencePacket, _silencePacket.Length).ConfigureAwait(false); - lastTick = thisTick; - } - else if (remaining > 1) - { - int millis = (int)Math.Floor(remaining / ticksPerMilli); - await Task.Delay(millis).ConfigureAwait(false); + nextTick += ticksPerFrame; } + else if (dist > 1) + await Task.Delay((int)dist).ConfigureAwait(false); } }); } From 64ddcb564bbf1781364ec749601af6cfc8d46db5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 30 Dec 2016 18:21:00 -0400 Subject: [PATCH 129/263] Added isDisposed checks to audio classes. Dont flush the OpusEncodeStream --- src/Discord.Net.WebSocket/Audio/AudioClient.cs | 13 ++++++++----- .../Audio/Streams/OpusEncodeStream.cs | 4 ++-- .../Audio/Streams/RTPWriteStream.cs | 7 ++++++- .../Entities/Guilds/SocketGuild.cs | 1 - 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 0f2f50309..e18995e86 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -43,9 +43,9 @@ namespace Discord.Audio private Task _heartbeatTask; private long _heartbeatTime; private string _url; - private bool _isDisposed; private uint _ssrc; private byte[] _secretKey; + private bool _isDisposed; public SocketGuild Guild { get; } public DiscordVoiceAPIClient ApiClient { get; private set; } @@ -165,8 +165,9 @@ namespace Discord.Audio ConnectionState = ConnectionState.Disconnected; await _audioLogger.InfoAsync("Disconnected").ConfigureAwait(false); - await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); + + await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } public Stream CreateOpusStream(int samplesPerFrame) @@ -314,10 +315,12 @@ namespace Discord.Audio internal void Dispose(bool disposing) { - DisconnectInternalAsync(null).GetAwaiter().GetResult(); - if (!_isDisposed) + if (disposing && !_isDisposed) + { _isDisposed = true; - ApiClient.Dispose(); + DisconnectInternalAsync(null).GetAwaiter().GetResult(); + ApiClient.Dispose(); + } } /// public void Dispose() => Dispose(true); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index a7c6e45a0..570c4e73c 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -54,7 +54,7 @@ namespace Discord.Audio } } - public override void Flush() + /*public override void Flush() { FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); } @@ -68,7 +68,7 @@ namespace Discord.Audio catch (Exception) { } //Incomplete frame _partialFramePos = 0; await base.FlushAsync(cancellationToken).ConfigureAwait(false); - } + }*/ protected override void Dispose(bool disposing) { diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 8317d578b..c3e42d791 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -11,6 +11,7 @@ namespace Discord.Audio private readonly byte[] _nonce, _secretKey; private int _samplesPerFrame; private uint _ssrc, _timestamp = 0; + private bool _isDisposed; protected readonly byte[] _buffer; @@ -36,7 +37,11 @@ namespace Discord.Audio protected override void Dispose(bool disposing) { - Flush(); + if (disposing && !_isDisposed) + { + _isDisposed = true; + Flush(); + } base.Dispose(disposing); } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 428171212..048b86a4a 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -479,7 +479,6 @@ namespace Discord.WebSocket if (AudioClient != null) await AudioClient.DisconnectAsync().ConfigureAwait(false); AudioClient = null; - await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, null, false, false).ConfigureAwait(false); } internal async Task FinishConnectAudio(int id, string url, string token) { From c34841c53d8ce92c092956446ac24c7ecba03f33 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 31 Dec 2016 06:31:56 -0400 Subject: [PATCH 130/263] Dont flush audio streams on Dispose --- .../Audio/Streams/RTPWriteStream.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index c3e42d791..300cd194c 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -11,7 +11,6 @@ namespace Discord.Audio private readonly byte[] _nonce, _secretKey; private int _samplesPerFrame; private uint _ssrc, _timestamp = 0; - private bool _isDisposed; protected readonly byte[] _buffer; @@ -35,16 +34,6 @@ namespace Discord.Audio _nonce[11] = (byte)(_ssrc >> 0); } - protected override void Dispose(bool disposing) - { - if (disposing && !_isDisposed) - { - _isDisposed = true; - Flush(); - } - base.Dispose(disposing); - } - public override void Write(byte[] buffer, int offset, int count) { WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); From 58cb8cfb20fe553373fd8e116bbf5987502af741 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 31 Dec 2016 20:07:20 -0400 Subject: [PATCH 131/263] Added a limit to BufferedAudioTarget's internal buffer. --- src/Discord.Net.Core/Audio/IAudioClient.cs | 4 +- .../Audio/AudioClient.cs | 14 +-- .../Audio/Targets/BufferedAudioTarget.cs | 91 +++++++++++++------ .../Entities/Guilds/SocketGuild.cs | 20 ++-- 4 files changed, 82 insertions(+), 47 deletions(-) diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 5e7aa2f6e..38e25d7b5 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -22,7 +22,7 @@ namespace Discord.Audio /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// - Stream CreateOpusStream(int samplesPerFrame); + Stream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000); /// /// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer. /// @@ -35,7 +35,7 @@ namespace Discord.Audio /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// /// - Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); + Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000); /// /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. /// diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index e18995e86..dd01aa2f9 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace Discord.Audio { - public class AudioClient : IAudioClient, IDisposable + internal class AudioClient : IAudioClient, IDisposable { public event Func Connected { @@ -74,7 +74,7 @@ namespace Discord.Audio ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); - ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); + //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedPacket += ProcessPacketAsync; ApiClient.Disconnected += async ex => @@ -170,10 +170,10 @@ namespace Discord.Audio await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } - public Stream CreateOpusStream(int samplesPerFrame) + public Stream CreateOpusStream(int samplesPerFrame, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } public Stream CreateDirectOpusStream(int samplesPerFrame) @@ -182,13 +182,13 @@ namespace Discord.Audio var target = new DirectAudioTarget(ApiClient); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } - public Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null) + public Stream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, _cancelTokenSource.Token); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); } - public Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null) + public Stream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate) { CheckSamplesPerFrame(samplesPerFrame); var target = new DirectAudioTarget(ApiClient); diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index 32b7c2c4d..d302dca87 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; @@ -8,62 +7,98 @@ namespace Discord.Audio { internal class BufferedAudioTarget : IAudioTarget, IDisposable { - private static readonly byte[] _silencePacket = new byte[] { 0xF8, 0xFF, 0xFE }; + private struct Frame + { + public Frame(byte[] buffer, int bytes) + { + Buffer = buffer; + Bytes = bytes; + } + + public readonly byte[] Buffer; + public readonly int Bytes; + } + + private static readonly byte[] _silenceFrame = new byte[] { 0xF8, 0xFF, 0xFE }; private Task _task; private DiscordVoiceAPIClient _client; private CancellationTokenSource _cancelTokenSource; - private ConcurrentQueue _queue; + private CancellationToken _cancelToken; + private ConcurrentQueue _queuedFrames; + private ConcurrentQueue _bufferPool; + private SemaphoreSlim _queueLock; + private int _ticksPerFrame; - internal BufferedAudioTarget(DiscordVoiceAPIClient client, int samplesPerFrame, CancellationToken cancelToken) + internal BufferedAudioTarget(DiscordVoiceAPIClient client, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken) { _client = client; - long ticksPerFrame = samplesPerFrame / 48; + _ticksPerFrame = samplesPerFrame / 48; + int queueLength = (bufferMillis + (_ticksPerFrame - 1)) / _ticksPerFrame; //Round up _cancelTokenSource = new CancellationTokenSource(); - cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; - _queue = new ConcurrentQueue(); //TODO: We need a better queue + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; + _queuedFrames = new ConcurrentQueue(); + _bufferPool = new ConcurrentQueue(); + for (int i = 0; i < queueLength; i++) + _bufferPool.Enqueue(new byte[1275]); + _queueLock = new SemaphoreSlim(queueLength, queueLength); - _task = Run(ticksPerFrame, cancelToken); + _task = Run(); } - private Task Run(long ticksPerFrame, CancellationToken cancelToken) + private Task Run() { return Task.Run(async () => { - long nextTick = Environment.TickCount; - while (!cancelToken.IsCancellationRequested) + try { - long tick = Environment.TickCount; - long dist = nextTick - tick; - if (dist <= 0) + long nextTick = Environment.TickCount; + while (!_cancelToken.IsCancellationRequested) { - byte[] buffer; - if (_queue.TryDequeue(out buffer)) - await _client.SendAsync(buffer, buffer.Length).ConfigureAwait(false); - else - await _client.SendAsync(_silencePacket, _silencePacket.Length).ConfigureAwait(false); - nextTick += ticksPerFrame; + long tick = Environment.TickCount; + long dist = nextTick - tick; + if (dist <= 0) + { + Frame frame; + if (_queuedFrames.TryDequeue(out frame)) + { +#if NETSTANDARD1_3 + Console.WriteLine("Pop"); +#endif + await _client.SendAsync(frame.Buffer, frame.Bytes).ConfigureAwait(false); + _bufferPool.Enqueue(frame.Buffer); + _queueLock.Release(); + } + else + await _client.SendAsync(_silenceFrame, _silenceFrame.Length).ConfigureAwait(false); + nextTick += _ticksPerFrame; + } + else if (dist > 1) + await Task.Delay((int)dist).ConfigureAwait(false); } - else if (dist > 1) - await Task.Delay((int)dist).ConfigureAwait(false); } + catch (OperationCanceledException) { } }); } - public Task SendAsync(byte[] buffer, int count) + public async Task SendAsync(byte[] data, int count) { - byte[] newBuffer = new byte[count]; - Buffer.BlockCopy(buffer, 0, newBuffer, 0, count); - _queue.Enqueue(newBuffer); - return Task.Delay(0); + await _queueLock.WaitAsync(-1, _cancelToken).ConfigureAwait(false); +#if NETSTANDARD1_3 + Console.WriteLine("Push"); +#endif + byte[] buffer; + _bufferPool.TryDequeue(out buffer); + Buffer.BlockCopy(data, 0, buffer, 0, count); + _queuedFrames.Enqueue(new Frame(buffer, count)); } public async Task FlushAsync() { while (true) { - if (_queue.Count == 0) + if (_queuedFrames.Count == 0) return; await Task.Delay(250).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 048b86a4a..eddee15c7 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -32,6 +32,7 @@ namespace Discord.WebSocket private ConcurrentDictionary _voiceStates; private ImmutableArray _emojis; private ImmutableArray _features; + private AudioClient _audioClient; internal bool _available; public string Name { get; private set; } @@ -42,7 +43,6 @@ namespace Discord.WebSocket public DefaultMessageNotifications DefaultMessageNotifications { get; private set; } public int MemberCount { get; set; } public int DownloadedMemberCount { get; private set; } - public AudioClient AudioClient { get; private set; } public ulong? AFKChannelId { get; private set; } public ulong? EmbedChannelId { get; private set; } @@ -59,6 +59,7 @@ namespace Discord.WebSocket public bool IsSynced => _syncPromise.Task.IsCompleted; public Task SyncPromise => _syncPromise.Task; public Task DownloaderPromise => _downloaderPromise.Task; + public IAudioClient AudioClient => _audioClient; public SocketGuildUser CurrentUser { get @@ -69,7 +70,6 @@ namespace Discord.WebSocket return null; } } - public SocketRole EveryoneRole => GetRole(Id); public IReadOnlyCollection Channels { @@ -476,9 +476,9 @@ namespace Discord.WebSocket { _audioConnectPromise?.TrySetCanceledAsync(); //Cancel any previous audio connection _audioConnectPromise = null; - if (AudioClient != null) - await AudioClient.DisconnectAsync().ConfigureAwait(false); - AudioClient = null; + if (_audioClient != null) + await _audioClient.DisconnectAsync().ConfigureAwait(false); + _audioClient = null; } internal async Task FinishConnectAudio(int id, string url, string token) { @@ -487,7 +487,7 @@ namespace Discord.WebSocket await _audioLock.WaitAsync().ConfigureAwait(false); try { - if (AudioClient == null) + if (_audioClient == null) { var audioClient = new AudioClient(this, id); var promise = _audioConnectPromise; @@ -497,7 +497,7 @@ namespace Discord.WebSocket if (!promise.Task.IsCompleted) { try { audioClient.Dispose(); } catch { } - AudioClient = null; + _audioClient = null; if (ex != null) await promise.TrySetExceptionAsync(ex); else @@ -535,10 +535,10 @@ namespace Discord.WebSocket _audioLock.Release(); }*/ }; - AudioClient = audioClient; + _audioClient = audioClient; } - await AudioClient.ConnectAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); - await _audioConnectPromise.TrySetResultAsync(AudioClient).ConfigureAwait(false); + await _audioClient.ConnectAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); + await _audioConnectPromise.TrySetResultAsync(_audioClient).ConfigureAwait(false); } catch (OperationCanceledException) { From b17a9b713f182f87ba1331f1639b64f85524ba42 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 31 Dec 2016 20:21:45 -0400 Subject: [PATCH 132/263] Removed debug lines --- .../Audio/Targets/BufferedAudioTarget.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index d302dca87..65ab537e0 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -63,9 +63,6 @@ namespace Discord.Audio Frame frame; if (_queuedFrames.TryDequeue(out frame)) { -#if NETSTANDARD1_3 - Console.WriteLine("Pop"); -#endif await _client.SendAsync(frame.Buffer, frame.Bytes).ConfigureAwait(false); _bufferPool.Enqueue(frame.Buffer); _queueLock.Release(); @@ -85,9 +82,6 @@ namespace Discord.Audio public async Task SendAsync(byte[] data, int count) { await _queueLock.WaitAsync(-1, _cancelToken).ConfigureAwait(false); -#if NETSTANDARD1_3 - Console.WriteLine("Push"); -#endif byte[] buffer; _bufferPool.TryDequeue(out buffer); Buffer.BlockCopy(data, 0, buffer, 0, count); From fc8d2b3155d35721f4459df60d055685c55371c6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 00:30:13 -0400 Subject: [PATCH 133/263] Added AudioOutStream, exposed Clear/ClearAsync methods. Added support for cancelTokens. --- src/Discord.Net.Core/Audio/AudioOutStream.cs | 16 ++++++++++++++++ src/Discord.Net.Core/Audio/IAudioClient.cs | 8 ++++---- src/Discord.Net.WebSocket/Audio/AudioClient.cs | 8 ++++---- .../Audio/Streams/RTPWriteStream.cs | 18 ++++++++++++------ .../Audio/Targets/BufferedAudioTarget.cs | 16 ++++++++++++---- .../Audio/Targets/DirectAudioTarget.cs | 7 +++++-- .../Audio/Targets/IAudioTarget.cs | 6 ++++-- 7 files changed, 57 insertions(+), 22 deletions(-) create mode 100644 src/Discord.Net.Core/Audio/AudioOutStream.cs diff --git a/src/Discord.Net.Core/Audio/AudioOutStream.cs b/src/Discord.Net.Core/Audio/AudioOutStream.cs new file mode 100644 index 000000000..dd91b71ee --- /dev/null +++ b/src/Discord.Net.Core/Audio/AudioOutStream.cs @@ -0,0 +1,16 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio +{ + public abstract class AudioOutStream : Stream + { + public override bool CanRead => false; + public override bool CanSeek => false; + public override bool CanWrite => true; + + public virtual void Clear() { } + public virtual Task ClearAsync(CancellationToken cancelToken) { return Task.Delay(0); } + } +} diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 38e25d7b5..dfc6f60bd 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -22,26 +22,26 @@ namespace Discord.Audio /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// - Stream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000); + AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis = 1000); /// /// Creates a new outgoing stream accepting Opus-encoded data. This is a direct stream with no internal timer. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// - Stream CreateDirectOpusStream(int samplesPerFrame); + AudioOutStream CreateDirectOpusStream(int samplesPerFrame); /// /// Creates a new outgoing stream accepting PCM (raw) data. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// /// - Stream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000); + AudioOutStream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000); /// /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// /// - Stream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); + AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index dd01aa2f9..784cdf194 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -170,25 +170,25 @@ namespace Discord.Audio await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } - public Stream CreateOpusStream(int samplesPerFrame, int bufferMillis) + public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } - public Stream CreateDirectOpusStream(int samplesPerFrame) + public AudioOutStream CreateDirectOpusStream(int samplesPerFrame) { CheckSamplesPerFrame(samplesPerFrame); var target = new DirectAudioTarget(ApiClient); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } - public Stream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis) + public AudioOutStream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); } - public Stream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate) + public AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate) { CheckSamplesPerFrame(samplesPerFrame); var target = new DirectAudioTarget(ApiClient); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 300cd194c..7ba95c591 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; namespace Discord.Audio { - internal class RTPWriteStream : Stream + internal class RTPWriteStream : AudioOutStream { private readonly IAudioTarget _target; private readonly byte[] _nonce, _secretKey; @@ -14,10 +14,6 @@ namespace Discord.Audio protected readonly byte[] _buffer; - public override bool CanRead => false; - public override bool CanSeek => false; - public override bool CanWrite => true; - internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc) { _target = target; @@ -40,6 +36,7 @@ namespace Discord.Audio } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); unchecked { if (_nonce[3]++ == byte.MaxValue) @@ -63,7 +60,16 @@ namespace Discord.Audio } public override async Task FlushAsync(CancellationToken cancellationToken) { - await _target.FlushAsync().ConfigureAwait(false); + await _target.FlushAsync(cancellationToken).ConfigureAwait(false); + } + + public override void Clear() + { + ClearAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task ClearAsync(CancellationToken cancelToken) + { + await _target.ClearAsync(cancelToken).ConfigureAwait(false); } public override long Length { get { throw new NotSupportedException(); } } diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs index 65ab537e0..b27c5c8b3 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs @@ -87,17 +87,25 @@ namespace Discord.Audio Buffer.BlockCopy(data, 0, buffer, 0, count); _queuedFrames.Enqueue(new Frame(buffer, count)); } - - public async Task FlushAsync() + + public async Task FlushAsync(CancellationToken cancelToken) { while (true) { + cancelToken.ThrowIfCancellationRequested(); if (_queuedFrames.Count == 0) return; - await Task.Delay(250).ConfigureAwait(false); + await Task.Delay(250, cancelToken).ConfigureAwait(false); } } - + public Task ClearAsync(CancellationToken cancelToken) + { + Frame ignored; + do + cancelToken.ThrowIfCancellationRequested(); + while (_queuedFrames.TryDequeue(out ignored)); + return Task.Delay(0); + } protected void Dispose(bool disposing) { if (disposing) diff --git a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs index c5b0983d0..2440fc0a8 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio { @@ -13,7 +14,9 @@ namespace Discord.Audio public Task SendAsync(byte[] buffer, int count) => _client.SendAsync(buffer, count); - public Task FlushAsync() + public Task FlushAsync(CancellationToken cancelToken) + => Task.Delay(0); + public Task ClearAsync(CancellationToken cancelToken) => Task.Delay(0); } } diff --git a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs index 8c4384550..1aa0d4ade 100644 --- a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs +++ b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs @@ -1,10 +1,12 @@ -using System.Threading.Tasks; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio { internal interface IAudioTarget { Task SendAsync(byte[] buffer, int count); - Task FlushAsync(); + Task FlushAsync(CancellationToken cancelToken); + Task ClearAsync(CancellationToken cancelToken); } } From dac51db29949cdabf1d271eedc774d48fe7e1581 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 09:03:35 -0400 Subject: [PATCH 134/263] Added DiscordShardedClient, some DiscordSocketClient fixes. --- .../Rest/GetBotGatewayResponse.cs | 13 + src/Discord.Net.Core/DiscordConfig.cs | 3 + src/Discord.Net.Rest/BaseDiscordClient.cs | 4 +- .../Commands/ShardedCommandContext.cs | 31 ++ .../DiscordShardedClient.Events.cs | 199 ++++++++++ .../DiscordShardedClient.cs | 364 ++++++++++++++++++ .../DiscordSocketApiClient.cs | 5 + .../DiscordSocketClient.cs | 28 +- .../DiscordSocketConfig.cs | 6 +- 9 files changed, 633 insertions(+), 20 deletions(-) create mode 100644 src/Discord.Net.API/Rest/GetBotGatewayResponse.cs create mode 100644 src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs create mode 100644 src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs create mode 100644 src/Discord.Net.WebSocket/DiscordShardedClient.cs diff --git a/src/Discord.Net.API/Rest/GetBotGatewayResponse.cs b/src/Discord.Net.API/Rest/GetBotGatewayResponse.cs new file mode 100644 index 000000000..d6d2bed00 --- /dev/null +++ b/src/Discord.Net.API/Rest/GetBotGatewayResponse.cs @@ -0,0 +1,13 @@ +#pragma warning disable CS1591 +using Newtonsoft.Json; + +namespace Discord.API.Rest +{ + public class GetBotGatewayResponse + { + [JsonProperty("url")] + public string Url { get; set; } + [JsonProperty("shards")] + public int Shards { get; set; } + } +} diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index bb7077472..78a5b0e1e 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -24,5 +24,8 @@ namespace Discord /// Gets or sets the minimum log level severity that will be sent to the LogMessage event. public LogSeverity LogLevel { get; set; } = LogSeverity.Info; + + /// Gets or sets whether the initial log entry should be printed. + internal bool DisplayInitialLog { get; set; } = true; } } diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 1a0348d34..c36031539 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -11,7 +11,7 @@ namespace Discord.Rest public abstract class BaseDiscordClient : IDiscordClient { public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } - private readonly AsyncEvent> _logEvent = new AsyncEvent>(); + internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); public event Func LoggedIn { add { _loggedInEvent.Add(value); } remove { _loggedInEvent.Remove(value); } } private readonly AsyncEvent> _loggedInEvent = new AsyncEvent>(); @@ -38,7 +38,7 @@ namespace Discord.Rest _connectionLock = new SemaphoreSlim(1, 1); _restLogger = LogManager.CreateLogger("Rest"); _queueLogger = LogManager.CreateLogger("Queue"); - _isFirstLogin = true; + _isFirstLogin = config.DisplayInitialLog; ApiClient.RequestQueue.RateLimitTriggered += async (id, info) => { diff --git a/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs b/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs new file mode 100644 index 000000000..627b9b390 --- /dev/null +++ b/src/Discord.Net.WebSocket/Commands/ShardedCommandContext.cs @@ -0,0 +1,31 @@ +using Discord.WebSocket; + +namespace Discord.Commands +{ + public class ShardedCommandContext : ICommandContext + { + public DiscordShardedClient Client { get; } + public SocketGuild Guild { get; } + public ISocketMessageChannel Channel { get; } + public SocketUser User { get; } + public SocketUserMessage Message { get; } + + public bool IsPrivate => Channel is IPrivateChannel; + + public ShardedCommandContext(DiscordShardedClient client, SocketUserMessage msg) + { + Client = client; + Guild = (msg.Channel as SocketGuildChannel)?.Guild; + Channel = msg.Channel; + User = msg.Author; + Message = msg; + } + + //ICommandContext + IDiscordClient ICommandContext.Client => Client; + IGuild ICommandContext.Guild => Guild; + IMessageChannel ICommandContext.Channel => Channel; + IUser ICommandContext.User => User; + IUserMessage ICommandContext.Message => Message; + } +} diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs new file mode 100644 index 000000000..449d30599 --- /dev/null +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs @@ -0,0 +1,199 @@ +using System; +using System.Threading.Tasks; + +namespace Discord.WebSocket +{ + //TODO: Add event docstrings + public partial class DiscordShardedClient + { + //Channels + public event Func ChannelCreated + { + add { _channelCreatedEvent.Add(value); } + remove { _channelCreatedEvent.Remove(value); } + } + private readonly AsyncEvent> _channelCreatedEvent = new AsyncEvent>(); + public event Func ChannelDestroyed + { + add { _channelDestroyedEvent.Add(value); } + remove { _channelDestroyedEvent.Remove(value); } + } + private readonly AsyncEvent> _channelDestroyedEvent = new AsyncEvent>(); + public event Func ChannelUpdated + { + add { _channelUpdatedEvent.Add(value); } + remove { _channelUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _channelUpdatedEvent = new AsyncEvent>(); + + //Messages + public event Func MessageReceived + { + add { _messageReceivedEvent.Add(value); } + remove { _messageReceivedEvent.Remove(value); } + } + private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); + public event Func, Task> MessageDeleted + { + add { _messageDeletedEvent.Add(value); } + remove { _messageDeletedEvent.Remove(value); } + } + private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>(); + public event Func, SocketMessage, Task> MessageUpdated + { + add { _messageUpdatedEvent.Add(value); } + remove { _messageUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); + public event Func, SocketReaction, Task> ReactionAdded + { + add { _reactionAddedEvent.Add(value); } + remove { _reactionAddedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, SocketReaction, Task> ReactionRemoved + { + add { _reactionRemovedEvent.Add(value); } + remove { _reactionRemovedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, Task> ReactionsCleared + { + add { _reactionsClearedEvent.Add(value); } + remove { _reactionsClearedEvent.Remove(value); } + } + private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); + + //Roles + public event Func RoleCreated + { + add { _roleCreatedEvent.Add(value); } + remove { _roleCreatedEvent.Remove(value); } + } + private readonly AsyncEvent> _roleCreatedEvent = new AsyncEvent>(); + public event Func RoleDeleted + { + add { _roleDeletedEvent.Add(value); } + remove { _roleDeletedEvent.Remove(value); } + } + private readonly AsyncEvent> _roleDeletedEvent = new AsyncEvent>(); + public event Func RoleUpdated + { + add { _roleUpdatedEvent.Add(value); } + remove { _roleUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _roleUpdatedEvent = new AsyncEvent>(); + + //Guilds + public event Func JoinedGuild + { + add { _joinedGuildEvent.Add(value); } + remove { _joinedGuildEvent.Remove(value); } + } + private AsyncEvent> _joinedGuildEvent = new AsyncEvent>(); + public event Func LeftGuild + { + add { _leftGuildEvent.Add(value); } + remove { _leftGuildEvent.Remove(value); } + } + private AsyncEvent> _leftGuildEvent = new AsyncEvent>(); + public event Func GuildAvailable + { + add { _guildAvailableEvent.Add(value); } + remove { _guildAvailableEvent.Remove(value); } + } + private AsyncEvent> _guildAvailableEvent = new AsyncEvent>(); + public event Func GuildUnavailable + { + add { _guildUnavailableEvent.Add(value); } + remove { _guildUnavailableEvent.Remove(value); } + } + private AsyncEvent> _guildUnavailableEvent = new AsyncEvent>(); + public event Func GuildMembersDownloaded + { + add { _guildMembersDownloadedEvent.Add(value); } + remove { _guildMembersDownloadedEvent.Remove(value); } + } + private AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>(); + public event Func GuildUpdated + { + add { _guildUpdatedEvent.Add(value); } + remove { _guildUpdatedEvent.Remove(value); } + } + private AsyncEvent> _guildUpdatedEvent = new AsyncEvent>(); + + //Users + public event Func UserJoined + { + add { _userJoinedEvent.Add(value); } + remove { _userJoinedEvent.Remove(value); } + } + private readonly AsyncEvent> _userJoinedEvent = new AsyncEvent>(); + public event Func UserLeft + { + add { _userLeftEvent.Add(value); } + remove { _userLeftEvent.Remove(value); } + } + private readonly AsyncEvent> _userLeftEvent = new AsyncEvent>(); + public event Func UserBanned + { + add { _userBannedEvent.Add(value); } + remove { _userBannedEvent.Remove(value); } + } + private readonly AsyncEvent> _userBannedEvent = new AsyncEvent>(); + public event Func UserUnbanned + { + add { _userUnbannedEvent.Add(value); } + remove { _userUnbannedEvent.Remove(value); } + } + private readonly AsyncEvent> _userUnbannedEvent = new AsyncEvent>(); + public event Func UserUpdated + { + add { _userUpdatedEvent.Add(value); } + remove { _userUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _userUpdatedEvent = new AsyncEvent>(); + public event Func GuildMemberUpdated + { + add { _guildMemberUpdatedEvent.Add(value); } + remove { _guildMemberUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _guildMemberUpdatedEvent = new AsyncEvent>(); + public event Func, SocketUser, SocketPresence, SocketPresence, Task> UserPresenceUpdated + { + add { _userPresenceUpdatedEvent.Add(value); } + remove { _userPresenceUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent, SocketUser, SocketPresence, SocketPresence, Task>> _userPresenceUpdatedEvent = new AsyncEvent, SocketUser, SocketPresence, SocketPresence, Task>>(); + public event Func UserVoiceStateUpdated + { + add { _userVoiceStateUpdatedEvent.Add(value); } + remove { _userVoiceStateUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _userVoiceStateUpdatedEvent = new AsyncEvent>(); + public event Func CurrentUserUpdated + { + add { _selfUpdatedEvent.Add(value); } + remove { _selfUpdatedEvent.Remove(value); } + } + private readonly AsyncEvent> _selfUpdatedEvent = new AsyncEvent>(); + public event Func UserIsTyping + { + add { _userIsTypingEvent.Add(value); } + remove { _userIsTypingEvent.Remove(value); } + } + private readonly AsyncEvent> _userIsTypingEvent = new AsyncEvent>(); + public event Func RecipientAdded + { + add { _recipientAddedEvent.Add(value); } + remove { _recipientAddedEvent.Remove(value); } + } + private readonly AsyncEvent> _recipientAddedEvent = new AsyncEvent>(); + public event Func RecipientRemoved + { + add { _recipientRemovedEvent.Add(value); } + remove { _recipientRemovedEvent.Remove(value); } + } + private readonly AsyncEvent> _recipientRemovedEvent = new AsyncEvent>(); + } +} diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs new file mode 100644 index 000000000..403f1e239 --- /dev/null +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -0,0 +1,364 @@ +using Discord.API; +using Discord.Rest; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Discord.WebSocket +{ + public partial class DiscordShardedClient : BaseDiscordClient, IDiscordClient + { + private readonly DiscordSocketConfig _baseConfig; + private int[] _shardIds; + private Dictionary _shardIdsToIndex; + private DiscordSocketClient[] _shards; + private int _totalShards; + private bool _automaticShards; + + /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. + public int Latency { get; private set; } + internal UserStatus Status => _shards[0].Status; + internal Game? Game => _shards[0].Game; + + public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; + public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } + public IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount()); + public IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(() => GetPrivateChannelCount()); + public IReadOnlyCollection Shards => _shards; + public IReadOnlyCollection VoiceRegions => _shards[0].VoiceRegions; + + /// Creates a new REST/WebSocket discord client. + public DiscordShardedClient() : this(null, new DiscordSocketConfig()) { } + /// Creates a new REST/WebSocket discord client. + public DiscordShardedClient(DiscordSocketConfig config) : this(null, config, CreateApiClient(config)) { } + /// Creates a new REST/WebSocket discord client. + public DiscordShardedClient(int[] ids) : this(ids, new DiscordSocketConfig()) { } + /// Creates a new REST/WebSocket discord client. + public DiscordShardedClient(int[] ids, DiscordSocketConfig config) : this(ids, config, CreateApiClient(config)) { } + private DiscordShardedClient(int[] ids, DiscordSocketConfig config, API.DiscordSocketApiClient client) + : base(config, client) + { + if (config.ShardId != null) + throw new ArgumentException($"{nameof(config.ShardId)} must not be set."); + if (ids != null && config.TotalShards == null) + throw new ArgumentException($"Custom ids are not supported when {nameof(config.TotalShards)} is not specified."); + + _shardIdsToIndex = new Dictionary(); + config.DisplayInitialLog = false; + _baseConfig = config; + + if (config.TotalShards == null) + _automaticShards = true; + else + { + _totalShards = config.TotalShards.Value; + _shardIds = ids ?? Enumerable.Range(0, _totalShards).ToArray(); + _shards = new DiscordSocketClient[_shardIds.Length]; + for (int i = 0; i < _shardIds.Length; i++) + { + _shardIdsToIndex.Add(_shardIds[i], i); + var newConfig = config.Clone(); + newConfig.ShardId = _shardIds[i]; + _shards[i] = new DiscordSocketClient(newConfig); + RegisterEvents(_shards[i]); + } + } + } + private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) + => new API.DiscordSocketApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, config.WebSocketProvider); + + protected override async Task OnLoginAsync(TokenType tokenType, string token) + { + if (_automaticShards) + { + var response = await ApiClient.GetBotGatewayAsync().ConfigureAwait(false); + _shardIds = Enumerable.Range(0, response.Shards).ToArray(); + _totalShards = _shardIds.Length; + _shards = new DiscordSocketClient[_shardIds.Length]; + for (int i = 0; i < _shardIds.Length; i++) + { + _shardIdsToIndex.Add(_shardIds[i], i); + var newConfig = _baseConfig.Clone(); + newConfig.ShardId = _shardIds[i]; + newConfig.TotalShards = _totalShards; + _shards[i] = new DiscordSocketClient(newConfig); + RegisterEvents(_shards[i]); + } + } + + //Assume threadsafe: already in a connection lock + for (int i = 0; i < _shards.Length; i++) + await _shards[i].LoginAsync(tokenType, token, false); + } + protected override async Task OnLogoutAsync() + { + //Assume threadsafe: already in a connection lock + for (int i = 0; i < _shards.Length; i++) + await _shards[i].LogoutAsync(); + + CurrentUser = null; + if (_automaticShards) + { + _shardIds = new int[0]; + _shardIdsToIndex.Clear(); + _totalShards = 0; + _shards = null; + } + } + + /// + public async Task ConnectAsync(bool waitForGuilds = true) + { + await _connectionLock.WaitAsync().ConfigureAwait(false); + try + { + await ConnectInternalAsync(waitForGuilds).ConfigureAwait(false); + } + catch + { + await DisconnectInternalAsync().ConfigureAwait(false); + throw; + } + finally { _connectionLock.Release(); } + } + private async Task ConnectInternalAsync(bool waitForGuilds) + { + for (int i = 0; i < _shards.Length; i++) + { + await _shards[i].ConnectAsync(waitForGuilds).ConfigureAwait(false); + if (i == 0) + CurrentUser = _shards[i].CurrentUser; + } + } + /// + public async Task DisconnectAsync() + { + await _connectionLock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync().ConfigureAwait(false); + } + finally { _connectionLock.Release(); } + } + private async Task DisconnectInternalAsync() + { + for (int i = 0; i < _shards.Length; i++) + await _shards[i].DisconnectAsync(); + } + + public DiscordSocketClient GetShard(int id) + { + for (int i = 0; i < _shards.Length; i++) + { + if (_shards[i].ShardId == id) + return _shards[i]; + } + return null; + } + private int GetShardIdFor(ulong guildId) + => (int)((guildId >> 22) % (uint)_totalShards); + private int GetShardIdFor(IGuild guild) + => GetShardIdFor(guild.Id); + private DiscordSocketClient GetShardFor(ulong guildId) + { + int id = GetShardIdFor(guildId); + if (_shardIdsToIndex.TryGetValue(id, out id)) + return _shards[id]; + return null; + } + private DiscordSocketClient GetShardFor(IGuild guild) + => GetShardFor(guild.Id); + + /// + public async Task GetApplicationInfoAsync() + => await _shards[0].GetApplicationInfoAsync().ConfigureAwait(false); + + /// + public SocketGuild GetGuild(ulong id) => GetShardFor(id).GetGuild(id); + /// + public Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null) + => ClientHelper.CreateGuildAsync(this, name, region, jpegIcon); + + /// + public SocketChannel GetChannel(ulong id) + { + for (int i = 0; i < _shards.Length; i++) + { + var channel = _shards[i].GetChannel(id); + if (channel != null) + return channel; + } + return null; + } + private IEnumerable GetPrivateChannels() + { + for (int i = 0; i < _shards.Length; i++) + { + foreach (var channel in _shards[i].PrivateChannels) + yield return channel; + } + } + private int GetPrivateChannelCount() + { + int result = 0; + for (int i = 0; i < _shards.Length; i++) + result += _shards[i].PrivateChannels.Count; + return result; + } + + /// + public Task> GetConnectionsAsync() + => ClientHelper.GetConnectionsAsync(this); + + private IEnumerable GetGuilds() + { + for (int i = 0; i < _shards.Length; i++) + { + foreach (var guild in _shards[i].Guilds) + yield return guild; + } + } + private int GetGuildCount() + { + int result = 0; + for (int i = 0; i < _shards.Length; i++) + result += _shards[i].Guilds.Count; + return result; + } + + /// + public Task GetInviteAsync(string inviteId) + => ClientHelper.GetInviteAsync(this, inviteId); + + /// + public SocketUser GetUser(ulong id) + { + for (int i = 0; i < _shards.Length; i++) + { + var user = _shards[i].GetUser(id); + if (user != null) + return user; + } + return null; + } + /// + public SocketUser GetUser(string username, string discriminator) + { + for (int i = 0; i < _shards.Length; i++) + { + var user = _shards[i].GetUser(username, discriminator); + if (user != null) + return user; + } + return null; + } + + /// + public RestVoiceRegion GetVoiceRegion(string id) + => _shards[0].GetVoiceRegion(id); + + /// Downloads the users list for all large guilds. + public async Task DownloadAllUsersAsync() + { + for (int i = 0; i < _shards.Length; i++) + await _shards[i].DownloadAllUsersAsync().ConfigureAwait(false); + } + /// Downloads the users list for the provided guilds, if they don't have a complete list. + public async Task DownloadUsersAsync(IEnumerable guilds) + { + for (int i = 0; i < _shards.Length; i++) + { + int id = _shardIds[i]; + var arr = guilds.Where(x => GetShardIdFor(x) == id).ToArray(); + if (arr.Length > 0) + await _shards[i].DownloadUsersAsync(arr); + } + } + + public async Task SetStatusAsync(UserStatus status) + { + for (int i = 0; i < _shards.Length; i++) + await _shards[i].SetStatusAsync(status).ConfigureAwait(false); + } + public async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) + { + for (int i = 0; i < _shards.Length; i++) + await _shards[i].SetGameAsync(name, streamUrl, streamType).ConfigureAwait(false); + } + + private void RegisterEvents(DiscordSocketClient client) + { + client.Log += (msg) => _logEvent.InvokeAsync(msg); + client.ChannelCreated += (channel) => _channelCreatedEvent.InvokeAsync(channel); + client.ChannelDestroyed += (channel) => _channelDestroyedEvent.InvokeAsync(channel); + client.ChannelUpdated += (oldChannel, newChannel) => _channelUpdatedEvent.InvokeAsync(oldChannel, newChannel); + + client.MessageReceived += (msg) => _messageReceivedEvent.InvokeAsync(msg); + client.MessageDeleted += (id, msg) => _messageDeletedEvent.InvokeAsync(id, msg); + client.MessageUpdated += (oldMsg, newMsg) => _messageUpdatedEvent.InvokeAsync(oldMsg, newMsg); + client.ReactionAdded += (id, msg, reaction) => _reactionAddedEvent.InvokeAsync(id, msg, reaction); + client.ReactionRemoved += (id, msg, reaction) => _reactionRemovedEvent.InvokeAsync(id, msg, reaction); + client.ReactionsCleared += (id, msg) => _reactionsClearedEvent.InvokeAsync(id, msg); + + client.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role); + client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role); + client.RoleUpdated += (oldRole, newRole) => _roleUpdatedEvent.InvokeAsync(oldRole, newRole); + + client.JoinedGuild += (guild) => _joinedGuildEvent.InvokeAsync(guild); + client.LeftGuild += (guild) => _leftGuildEvent.InvokeAsync(guild); + client.GuildAvailable += (guild) => _guildAvailableEvent.InvokeAsync(guild); + client.GuildUnavailable += (guild) => _guildUnavailableEvent.InvokeAsync(guild); + client.GuildMembersDownloaded += (guild) => _guildMembersDownloadedEvent.InvokeAsync(guild); + client.GuildUpdated += (oldGuild, newGuild) => _guildUpdatedEvent.InvokeAsync(oldGuild, newGuild); + + client.UserJoined += (user) => _userJoinedEvent.InvokeAsync(user); + client.UserLeft += (user) => _userLeftEvent.InvokeAsync(user); + client.UserBanned += (user, guild) => _userBannedEvent.InvokeAsync(user, guild); + client.UserUnbanned += (user, guild) => _userUnbannedEvent.InvokeAsync(user, guild); + client.UserUpdated += (oldUser, newUser) => _userUpdatedEvent.InvokeAsync(oldUser, newUser); + client.UserPresenceUpdated += (guild, user, oldPresence, newPresence) => _userPresenceUpdatedEvent.InvokeAsync(guild, user, oldPresence, newPresence); + client.UserVoiceStateUpdated += (user, oldVoiceState, newVoiceState) => _userVoiceStateUpdatedEvent.InvokeAsync(user, oldVoiceState, newVoiceState); + client.CurrentUserUpdated += (oldUser, newUser) => _selfUpdatedEvent.InvokeAsync(oldUser, newUser); + client.UserIsTyping += (oldUser, newUser) => _userIsTypingEvent.InvokeAsync(oldUser, newUser); + client.RecipientAdded += (user) => _recipientAddedEvent.InvokeAsync(user); + client.RecipientAdded += (user) => _recipientRemovedEvent.InvokeAsync(user); + } + + //IDiscordClient + Task IDiscordClient.ConnectAsync() + => ConnectAsync(); + + async Task IDiscordClient.GetApplicationInfoAsync() + => await GetApplicationInfoAsync().ConfigureAwait(false); + + Task IDiscordClient.GetChannelAsync(ulong id, CacheMode mode) + => Task.FromResult(GetChannel(id)); + Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode) + => Task.FromResult>(PrivateChannels); + + async Task> IDiscordClient.GetConnectionsAsync() + => await GetConnectionsAsync().ConfigureAwait(false); + + async Task IDiscordClient.GetInviteAsync(string inviteId) + => await GetInviteAsync(inviteId).ConfigureAwait(false); + + Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode) + => Task.FromResult(GetGuild(id)); + Task> IDiscordClient.GetGuildsAsync(CacheMode mode) + => Task.FromResult>(Guilds); + async Task IDiscordClient.CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon) + => await CreateGuildAsync(name, region, jpegIcon).ConfigureAwait(false); + + Task IDiscordClient.GetUserAsync(ulong id, CacheMode mode) + => Task.FromResult(GetUser(id)); + Task IDiscordClient.GetUserAsync(string username, string discriminator) + => Task.FromResult(GetUser(username, discriminator)); + + Task> IDiscordClient.GetVoiceRegionsAsync() + => Task.FromResult>(VoiceRegions); + Task IDiscordClient.GetVoiceRegionAsync(string id) + => Task.FromResult(GetVoiceRegion(id)); + } +} diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 1ab67d724..bcc8e40b7 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -178,6 +178,11 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); return await SendAsync("GET", () => "gateway", new BucketIds(), options: options).ConfigureAwait(false); } + public async Task GetBotGatewayAsync(RequestOptions options = null) + { + options = RequestOptions.CreateOrClone(options); + return await SendAsync("GET", () => "gateway/bot", new BucketIds(), options: options).ConfigureAwait(false); + } public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, int shardID = 0, int totalShards = 1, RequestOptions options = null) { options = RequestOptions.CreateOrClone(options); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index c0bbb750b..a93ba5fcf 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -3,7 +3,6 @@ using Discord.API.Gateway; using Discord.Audio; using Discord.Logging; using Discord.Net.Converters; -using Discord.Net.Queue; using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; @@ -65,6 +64,7 @@ namespace Discord.WebSocket public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } public IReadOnlyCollection Guilds => State.Guilds; public IReadOnlyCollection PrivateChannels => State.PrivateChannels; + public IReadOnlyCollection VoiceRegions => _voiceRegions.ToReadOnlyCollection(); /// Creates a new REST/WebSocket discord client. public DiscordSocketClient() : this(new DiscordSocketConfig()) { } @@ -73,8 +73,8 @@ namespace Discord.WebSocket private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client) : base(config, client) { - ShardId = config.ShardId; - TotalShards = config.TotalShards; + ShardId = config.ShardId ?? 0; + TotalShards = config.TotalShards ?? 1; MessageCacheSize = config.MessageCacheSize; LargeThreshold = config.LargeThreshold; AudioMode = config.AudioMode; @@ -85,7 +85,7 @@ namespace Discord.WebSocket State = new ClientState(0, 0); _nextAudioId = 1; - _gatewayLogger = LogManager.CreateLogger("Gateway"); + _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => @@ -206,7 +206,7 @@ namespace Discord.WebSocket await _connectTask.Task.ConfigureAwait(false); await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); - await SendStatus().ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); if (!isReconnecting) @@ -424,11 +424,7 @@ namespace Discord.WebSocket public Task DownloadAllUsersAsync() => DownloadUsersAsync(State.Guilds.Where(x => !x.HasAllMembers)); /// Downloads the users list for the provided guilds, if they don't have a complete list. - public Task DownloadUsersAsync(IEnumerable guilds) - => DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null)); - public Task DownloadUsersAsync(params IGuild[] guilds) - => DownloadUsersAsync(guilds.Select(x => x as SocketGuild).Where(x => x != null)); - private async Task DownloadUsersAsync(IEnumerable guilds) + public async Task DownloadUsersAsync(IEnumerable guilds) { var cachedGuilds = guilds.ToImmutableArray(); if (cachedGuilds.Length == 0) return; @@ -474,25 +470,25 @@ namespace Discord.WebSocket } } - public async Task SetStatus(UserStatus status) + public async Task SetStatusAsync(UserStatus status) { Status = status; if (status == UserStatus.AFK) _statusSince = DateTimeOffset.UtcNow; else _statusSince = null; - await SendStatus().ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); } - public async Task SetGame(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) + public async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) { if (name != null) Game = new Game(name, streamUrl, streamType); else Game = null; CurrentUser.Presence = new SocketPresence(Status, Game); - await SendStatus().ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); } - private async Task SendStatus() + private async Task SendStatusAsync() { var game = Game; var status = Status; @@ -1803,7 +1799,7 @@ namespace Discord.WebSocket => Task.FromResult(GetUser(username, discriminator)); Task> IDiscordClient.GetVoiceRegionsAsync() - => Task.FromResult>(_voiceRegions.ToReadOnlyCollection()); + => Task.FromResult>(VoiceRegions); Task IDiscordClient.GetVoiceRegionAsync(string id) => Task.FromResult(GetVoiceRegion(id)); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 04001a5aa..17640e25b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -14,9 +14,9 @@ namespace Discord.WebSocket public int ConnectionTimeout { get; set; } = 30000; /// Gets or sets the id for this shard. Must be less than TotalShards. - public int ShardId { get; set; } = 0; + public int? ShardId { get; set; } = null; /// Gets or sets the total number of shards for this application. - public int TotalShards { get; set; } = 1; + public int? TotalShards { get; set; } = null; /// Gets or sets the number of messages per channel that should be kept in cache. Setting this to zero disables the message cache entirely. public int MessageCacheSize { get; set; } = 0; @@ -54,5 +54,7 @@ namespace Discord.WebSocket }; #endif } + + internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; } } From e2934abe29cde238000d6cdd367388b5925044ce Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 23:28:42 -0400 Subject: [PATCH 135/263] Made API models internal. Removed Discord.Net.API. --- src/Discord.Net.API/AssemblyInfo.cs | 8 -- .../Discord.Net.Commands.csproj | 3 +- src/Discord.Net.Commands/ModuleBase.cs | 2 +- src/Discord.Net.Core/Audio/AudioInStream.cs | 8 ++ src/Discord.Net.Core/Discord.Net.Core.csproj | 3 - .../Entities/Channels}/Direction.cs | 0 .../Entities/Channels/IGuildChannel.cs | 3 +- .../Entities/Channels/IMessageChannel.cs | 2 +- .../Entities/Channels/ITextChannel.cs | 3 +- .../Entities/Channels/IVoiceChannel.cs | 3 +- .../Guilds}/DefaultMessageNotifications.cs | 0 .../Entities/Guilds/GuildEmoji.cs | 8 +- .../Entities/Guilds/IGuild.cs | 3 +- .../Entities/Guilds}/MfaLevel.cs | 0 .../Entities/Guilds}/PermissionTarget.cs | 0 .../Entities/Guilds}/VerificationLevel.cs | 0 src/Discord.Net.Core/Entities/Image.cs | 7 - .../Entities/Messages/Embed.cs | 62 +++++++++ .../Entities/Messages/EmbedAuthor.cs | 15 +-- .../Entities/Messages/EmbedField.cs | 13 +- .../Entities/Messages/EmbedFooter.cs | 13 +- .../Entities/Messages/EmbedImage.cs | 9 +- .../Entities/Messages/EmbedProvider.cs | 7 +- .../Entities/Messages/EmbedThumbnail.cs | 9 +- .../Entities/Messages/EmbedVideo.cs | 9 +- .../Entities/Messages/Emoji.cs | 14 +- .../Entities/Messages/MessageProperties.cs | 2 +- .../Entities/Messages}/MessageType.cs | 0 .../Entities/Permissions/Overwrite.cs | 7 +- src/Discord.Net.Core/Entities/Roles/IRole.cs | 3 +- src/Discord.Net.Core/Entities/Users/Game.cs | 7 - .../Entities/Users/IGuildUser.cs | 3 +- .../Entities/Users}/StreamType.cs | 0 .../Entities/Users}/UserStatus.cs | 0 .../Utils}/Optional.cs | 0 .../API}/Common/Application.cs | 2 +- .../API}/Common/Attachment.cs | 2 +- .../API}/Common/Ban.cs | 2 +- .../API}/Common/Channel.cs | 2 +- .../API}/Common/Connection.cs | 2 +- .../API}/Common/Embed.cs | 2 +- .../API}/Common/EmbedAuthor.cs | 2 +- .../API}/Common/EmbedField.cs | 2 +- .../API}/Common/EmbedFooter.cs | 2 +- .../API}/Common/EmbedImage.cs | 2 +- .../API}/Common/EmbedProvider.cs | 2 +- .../API}/Common/EmbedThumbnail.cs | 2 +- .../API}/Common/EmbedVideo.cs | 2 +- .../API}/Common/Emoji.cs | 2 +- .../API}/Common/Game.cs | 2 +- .../API}/Common/Guild.cs | 2 +- .../API}/Common/GuildEmbed.cs | 2 +- .../API}/Common/GuildMember.cs | 2 +- .../API}/Common/Integration.cs | 2 +- .../API}/Common/IntegrationAccount.cs | 2 +- .../API}/Common/Invite.cs | 2 +- .../API}/Common/InviteChannel.cs | 2 +- .../API}/Common/InviteGuild.cs | 2 +- .../API}/Common/InviteMetadata.cs | 2 +- .../API}/Common/Message.cs | 2 +- .../API}/Common/Overwrite.cs | 2 +- .../API}/Common/Presence.cs | 2 +- .../API}/Common/Reaction.cs | 8 +- .../API}/Common/ReadState.cs | 2 +- .../API}/Common/Relationship.cs | 2 +- .../API}/Common/RelationshipType.cs | 2 +- .../API}/Common/Role.cs | 2 +- .../API}/Common/User.cs | 2 +- .../API}/Common/UserGuild.cs | 2 +- .../API}/Common/VoiceRegion.cs | 2 +- .../API}/Common/VoiceState.cs | 2 +- .../API}/EntityOrId.cs | 2 +- .../API}/Image.cs | 2 +- .../API}/Int53Attribute.cs | 2 +- .../API}/Net/MultipartFile.cs | 2 +- .../API}/Rest/CreateChannelInviteParams.cs | 2 +- .../API}/Rest/CreateDMChannelParams.cs | 2 +- .../API}/Rest/CreateGuildBanParams.cs | 2 +- .../API}/Rest/CreateGuildChannelParams.cs | 2 +- .../API}/Rest/CreateGuildIntegrationParams.cs | 2 +- .../API}/Rest/CreateGuildParams.cs | 2 +- .../API}/Rest/CreateMessageParams.cs | 3 +- .../API}/Rest/DeleteMessagesParams.cs | 2 +- .../API}/Rest/GetBotGatewayResponse.cs | 2 +- .../API}/Rest/GetChannelMessagesParams.cs | 2 +- .../API}/Rest/GetGatewayResponse.cs | 2 +- .../API}/Rest/GetGuildMembersParams.cs | 2 +- .../API}/Rest/GetGuildPruneCountResponse.cs | 2 +- .../API}/Rest/GetReactionUsersParams.cs | 2 +- .../API}/Rest/GuildPruneParams.cs | 2 +- .../Rest/ModifyChannelPermissionsParams.cs | 2 +- .../API}/Rest/ModifyCurrentUserNickParams.cs | 2 +- .../API}/Rest/ModifyCurrentUserParams.cs | 2 +- .../API}/Rest/ModifyGuildChannelParams.cs | 2 +- .../API}/Rest/ModifyGuildChannelsParams.cs | 2 +- .../API}/Rest/ModifyGuildEmbedParams.cs | 2 +- .../API}/Rest/ModifyGuildIntegrationParams.cs | 2 +- .../API}/Rest/ModifyGuildMemberParams.cs | 2 +- .../API}/Rest/ModifyGuildParams.cs | 2 +- .../API}/Rest/ModifyGuildRoleParams.cs | 2 +- .../API}/Rest/ModifyGuildRolesParams.cs | 2 +- .../API}/Rest/ModifyMessageParams.cs | 2 +- .../API}/Rest/ModifyTextChannelParams.cs | 2 +- .../API}/Rest/ModifyVoiceChannelParams.cs | 2 +- .../API}/Rest/UploadFileParams.cs | 2 +- .../API}/RpcFrame.cs | 2 +- .../API}/SocketFrame.cs | 2 +- src/Discord.Net.Rest/BaseDiscordClient.cs | 2 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 1 - src/Discord.Net.Rest/DiscordRestApiClient.cs | 6 +- .../Entities/Channels/ChannelHelper.cs | 4 +- .../Entities/Channels}/ChannelType.cs | 0 .../Entities/Channels/IRestMessageChannel.cs | 2 +- .../Entities/Channels/RestDMChannel.cs | 4 +- .../Entities/Channels/RestGroupChannel.cs | 4 +- .../Entities/Channels/RestGuildChannel.cs | 6 +- .../Entities/Channels/RestTextChannel.cs | 7 +- .../Channels/RpcVirtualMessageChannel.cs | 4 +- .../Entities/Guilds/RestGuild.cs | 2 +- .../Entities/Guilds/RestGuildIntegration.cs | 5 +- .../Entities/Guilds/RestUserGuild.cs | 2 +- .../Entities/Messages/Embed.cs | 70 ---------- .../Entities/Messages/EmbedBuilder.cs | 86 ++++++------ .../Entities/Messages/MessageHelper.cs | 2 +- .../Entities/Messages/RestReaction.cs | 4 +- .../Entities/Messages/RestUserMessage.cs | 2 +- .../Entities/Users/RestUser.cs | 2 +- .../Extensions/EntityExtensions.cs | 124 ++++++++++++++++++ .../Net/Converters/ArrayConverter.cs | 0 .../Net/Converters/DiscordContractResolver.cs | 0 .../Net/Converters/ImageConverter.cs | 0 .../Net/Converters/NullableConverter.cs | 0 .../Net/Converters/OptionalConverter.cs | 0 .../Converters/PermissionTargetConverter.cs | 0 .../Net/Converters/StringEntityConverter.cs | 0 .../Net/Converters/UInt64Converter.cs | 0 .../Net/Converters/UInt64EntityConverter.cs | 0 .../Converters/UInt64EntityOrIdConverter.cs | 0 .../Net/Converters/UserStatusConverter.cs | 0 .../API}/Rpc/AuthenticateParams.cs | 2 +- .../API}/Rpc/AuthenticateResponse.cs | 2 +- .../API}/Rpc/AuthorizeParams.cs | 2 +- .../API}/Rpc/AuthorizeResponse.cs | 2 +- .../API}/Rpc/Channel.cs | 2 +- .../API}/Rpc/ChannelSubscriptionParams.cs | 2 +- .../API}/Rpc/ChannelSummary.cs | 2 +- .../API}/Rpc/ErrorEvent.cs | 2 +- .../API}/Rpc/ExtendedVoiceState.cs | 2 +- .../API}/Rpc/GetChannelParams.cs | 2 +- .../API}/Rpc/GetChannelsParams.cs | 2 +- .../API}/Rpc/GetChannelsResponse.cs | 2 +- .../API}/Rpc/GetGuildParams.cs | 2 +- .../API}/Rpc/GetGuildsParams.cs | 2 +- .../API}/Rpc/GetGuildsResponse.cs | 2 +- .../API}/Rpc/Guild.cs | 2 +- .../API}/Rpc/GuildMember.cs | 2 +- .../API}/Rpc/GuildStatusEvent.cs | 2 +- .../API}/Rpc/GuildSubscriptionParams.cs | 2 +- .../API}/Rpc/GuildSummary.cs | 2 +- .../API}/Rpc/Message.cs | 2 +- .../API}/Rpc/MessageEvent.cs | 2 +- .../API}/Rpc/Pan.cs | 2 +- .../API}/Rpc/ReadyEvent.cs | 2 +- .../API}/Rpc/RpcConfig.cs | 2 +- .../API}/Rpc/SelectChannelParams.cs | 2 +- .../API}/Rpc/SetLocalVolumeParams.cs | 2 +- .../API}/Rpc/SetLocalVolumeResponse.cs | 2 +- .../API}/Rpc/SpeakingEvent.cs | 2 +- .../API}/Rpc/SubscriptionResponse.cs | 2 +- .../API}/Rpc/UserVoiceSettings.cs | 2 +- .../API}/Rpc/VoiceDevice.cs | 2 +- .../API}/Rpc/VoiceDeviceSettings.cs | 2 +- .../API}/Rpc/VoiceMode.cs | 2 +- .../API}/Rpc/VoiceSettings.cs | 2 +- .../API}/Rpc/VoiceShortcut.cs | 2 +- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 1 - src/Discord.Net.Rpc/DiscordRpcApiClient.cs | 2 +- src/Discord.Net.Rpc/DiscordRpcClient.cs | 68 ++++++++-- .../Entities/Channels/RpcDMChannel.cs | 4 +- .../Entities/Channels/RpcGroupChannel.cs | 4 +- .../Entities/Channels/RpcTextChannel.cs | 4 +- .../Entities/Messages/RpcUserMessage.cs | 2 +- .../Entities/UserVoiceProperties.cs | 18 +++ src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 2 +- .../Entities/Users/RpcVoiceState.cs | 2 +- .../Entities/Users/UserVoiceProperties.cs | 10 ++ src/Discord.Net.Rpc/Entities/VoiceDevice.cs | 2 +- .../Entities/VoiceDeviceProperties.cs | 9 ++ .../Entities/VoiceModeProperties.cs | 11 ++ .../Entities/VoiceProperties.cs | 14 ++ src/Discord.Net.Rpc/Entities/VoiceShortcut.cs | 2 +- .../Entities}/VoiceShortcutType.cs | 0 .../Extensions/EntityExtensions.cs | 31 +++++ .../API}/Gateway/ExtendedGuild.cs | 2 +- .../API}/Gateway/GatewayOpCode.cs | 2 +- .../API}/Gateway/GuildBanEvent.cs | 2 +- .../API}/Gateway/GuildEmojiUpdateEvent.cs | 2 +- .../API}/Gateway/GuildMemberAddEvent.cs | 2 +- .../API}/Gateway/GuildMemberRemoveEvent.cs | 2 +- .../API}/Gateway/GuildMemberUpdateEvent.cs | 2 +- .../API}/Gateway/GuildMembersChunkEvent.cs | 2 +- .../API}/Gateway/GuildRoleCreateEvent.cs | 2 +- .../API}/Gateway/GuildRoleDeleteEvent.cs | 2 +- .../API}/Gateway/GuildRoleUpdateEvent.cs | 2 +- .../API}/Gateway/GuildSyncEvent.cs | 2 +- .../API}/Gateway/HelloEvent.cs | 2 +- .../API}/Gateway/IdentifyParams.cs | 2 +- .../API}/Gateway/MessageDeleteBulkEvent.cs | 2 +- .../API}/Gateway/Reaction.cs | 2 +- .../API}/Gateway/ReadyEvent.cs | 2 +- .../API}/Gateway/RecipientEvent.cs | 2 +- .../API}/Gateway/RemoveAllReactionsEvent.cs | 2 +- .../API}/Gateway/RequestMembersParams.cs | 2 +- .../API}/Gateway/ResumeParams.cs | 2 +- .../API}/Gateway/ResumedEvent.cs | 2 +- .../API}/Gateway/StatusUpdateParams.cs | 2 +- .../API}/Gateway/TypingStartEvent.cs | 2 +- .../API}/Gateway/VoiceServerUpdateEvent.cs | 2 +- .../API}/Gateway/VoiceStateUpdateParams.cs | 2 +- .../API}/Voice/IdentifyParams.cs | 2 +- .../API}/Voice/ReadyEvent.cs | 2 +- .../API}/Voice/SelectProtocolParams.cs | 2 +- .../API}/Voice/SessionDescriptionEvent.cs | 2 +- .../API}/Voice/SpeakingParams.cs | 2 +- .../API}/Voice/UdpProtocolInfo.cs | 2 +- .../API}/Voice/VoiceOpCode.cs | 2 +- .../Discord.Net.WebSocket.csproj | 1 - .../DiscordShardedClient.cs | 16 +-- .../DiscordSocketApiClient.cs | 2 +- .../DiscordSocketClient.cs | 2 +- .../DiscordVoiceApiClient.cs | 2 +- .../Channels/ISocketMessageChannel.cs | 2 +- .../Entities/Channels/SocketDMChannel.cs | 4 +- .../Entities/Channels/SocketGroupChannel.cs | 4 +- .../Entities/Channels/SocketGuildChannel.cs | 6 +- .../Entities/Channels/SocketTextChannel.cs | 4 +- .../Entities/Guilds/SocketGuild.cs | 4 +- .../Entities/Messages/SocketReaction.cs | 2 +- .../Entities/Messages/SocketUserMessage.cs | 2 +- .../Entities/Users/SocketPresence.cs | 4 +- .../Entities/Users/SocketUser.cs | 2 +- .../Entities/Users/SocketVoiceState.cs | 2 +- .../Extensions/EntityExtensions.cs | 12 ++ src/Discord.Net/Discord.Net.csproj | 3 +- 244 files changed, 641 insertions(+), 473 deletions(-) delete mode 100644 src/Discord.Net.API/AssemblyInfo.cs create mode 100644 src/Discord.Net.Core/Audio/AudioInStream.cs rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Channels}/Direction.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Guilds}/DefaultMessageNotifications.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Guilds}/MfaLevel.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Guilds}/PermissionTarget.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Guilds}/VerificationLevel.cs (100%) create mode 100644 src/Discord.Net.Core/Entities/Messages/Embed.cs rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Messages}/MessageType.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Users}/StreamType.cs (100%) rename src/{Discord.Net.API/Common => Discord.Net.Core/Entities/Users}/UserStatus.cs (100%) rename src/{Discord.Net.API => Discord.Net.Core/Utils}/Optional.cs (100%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Application.cs (95%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Attachment.cs (95%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Ban.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Channel.cs (98%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Connection.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Embed.cs (97%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedAuthor.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedField.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedFooter.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedImage.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedProvider.cs (87%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedThumbnail.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/EmbedVideo.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Emoji.cs (95%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Game.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Guild.cs (98%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/GuildEmbed.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/GuildMember.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Integration.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/IntegrationAccount.cs (86%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Invite.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/InviteChannel.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/InviteGuild.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/InviteMetadata.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Message.cs (98%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Overwrite.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Presence.cs (95%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Reaction.cs (62%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/ReadState.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Relationship.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/RelationshipType.cs (82%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/Role.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/User.cs (97%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/UserGuild.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/VoiceRegion.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Common/VoiceState.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/EntityOrId.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Image.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Int53Attribute.cs (71%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Net/MultipartFile.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateChannelInviteParams.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateDMChannelParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateGuildBanParams.cs (77%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateGuildChannelParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateGuildIntegrationParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateGuildParams.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/CreateMessageParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/DeleteMessagesParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetBotGatewayResponse.cs (85%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetChannelMessagesParams.cs (85%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetGatewayResponse.cs (81%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetGuildMembersParams.cs (81%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetGuildPruneCountResponse.cs (79%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GetReactionUsersParams.cs (78%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/GuildPruneParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyChannelPermissionsParams.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyCurrentUserNickParams.cs (88%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyCurrentUserParams.cs (88%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildChannelParams.cs (88%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildChannelsParams.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildEmbedParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildIntegrationParams.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildMemberParams.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildParams.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildRoleParams.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyGuildRolesParams.cs (82%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyMessageParams.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyTextChannelParams.cs (78%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/ModifyVoiceChannelParams.cs (82%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/Rest/UploadFileParams.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/RpcFrame.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rest/API}/SocketFrame.cs (93%) rename src/{Discord.Net.API/Common => Discord.Net.Rest/Entities/Channels}/ChannelType.cs (100%) delete mode 100644 src/Discord.Net.Rest/Entities/Messages/Embed.cs rename src/{Discord.Net.Core => Discord.Net.Rest}/Entities/Messages/EmbedBuilder.cs (58%) create mode 100644 src/Discord.Net.Rest/Extensions/EntityExtensions.cs rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/ArrayConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/DiscordContractResolver.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/ImageConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/NullableConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/OptionalConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/PermissionTargetConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/StringEntityConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/UInt64Converter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/UInt64EntityConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/UInt64EntityOrIdConverter.cs (100%) rename src/{Discord.Net.Core => Discord.Net.Rest}/Net/Converters/UserStatusConverter.cs (100%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/AuthenticateParams.cs (83%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/AuthenticateResponse.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/AuthorizeParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/AuthorizeResponse.cs (82%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/Channel.cs (97%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/ChannelSubscriptionParams.cs (80%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/ChannelSummary.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/ErrorEvent.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/ExtendedVoiceState.cs (93%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetChannelParams.cs (83%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetChannelsParams.cs (82%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetChannelsResponse.cs (86%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetGuildParams.cs (83%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetGuildsParams.cs (68%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GetGuildsResponse.cs (83%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/Guild.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GuildMember.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GuildStatusEvent.cs (87%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GuildSubscriptionParams.cs (80%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/GuildSummary.cs (86%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/Message.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/MessageEvent.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/Pan.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/ReadyEvent.cs (89%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/RpcConfig.cs (92%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/SelectChannelParams.cs (87%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/SetLocalVolumeParams.cs (81%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/SetLocalVolumeResponse.cs (85%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/SpeakingEvent.cs (84%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/SubscriptionResponse.cs (81%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/UserVoiceSettings.cs (91%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/VoiceDevice.cs (87%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/VoiceDeviceSettings.cs (90%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/VoiceMode.cs (94%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/VoiceSettings.cs (96%) rename src/{Discord.Net.API => Discord.Net.Rpc/API}/Rpc/VoiceShortcut.cs (91%) create mode 100644 src/Discord.Net.Rpc/Entities/UserVoiceProperties.cs create mode 100644 src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs create mode 100644 src/Discord.Net.Rpc/Entities/VoiceDeviceProperties.cs create mode 100644 src/Discord.Net.Rpc/Entities/VoiceModeProperties.cs create mode 100644 src/Discord.Net.Rpc/Entities/VoiceProperties.cs rename src/{Discord.Net.API/Rpc => Discord.Net.Rpc/Entities}/VoiceShortcutType.cs (100%) create mode 100644 src/Discord.Net.Rpc/Extensions/EntityExtensions.cs rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/ExtendedGuild.cs (94%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GatewayOpCode.cs (97%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildBanEvent.cs (88%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildEmojiUpdateEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildMemberAddEvent.cs (77%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildMemberRemoveEvent.cs (85%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildMemberUpdateEvent.cs (76%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildMembersChunkEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildRoleCreateEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildRoleDeleteEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildRoleUpdateEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/GuildSyncEvent.cs (92%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/HelloEvent.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/IdentifyParams.cs (95%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/MessageDeleteBulkEvent.cs (87%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/Reaction.cs (93%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/ReadyEvent.cs (97%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/RecipientEvent.cs (88%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/RemoveAllReactionsEvent.cs (84%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/RequestMembersParams.cs (91%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/ResumeParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/ResumedEvent.cs (85%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/StatusUpdateParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/TypingStartEvent.cs (90%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/VoiceServerUpdateEvent.cs (88%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Gateway/VoiceStateUpdateParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/IdentifyParams.cs (92%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/ReadyEvent.cs (93%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/SelectProtocolParams.cs (86%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/SessionDescriptionEvent.cs (85%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/SpeakingParams.cs (88%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/UdpProtocolInfo.cs (90%) rename src/{Discord.Net.API => Discord.Net.WebSocket/API}/Voice/VoiceOpCode.cs (96%) create mode 100644 src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs diff --git a/src/Discord.Net.API/AssemblyInfo.cs b/src/Discord.Net.API/AssemblyInfo.cs deleted file mode 100644 index 49872adaf..000000000 --- a/src/Discord.Net.API/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Discord.Net.Core")] -[assembly: InternalsVisibleTo("Discord.Net.Rest")] -[assembly: InternalsVisibleTo("Discord.Net.Rpc")] -[assembly: InternalsVisibleTo("Discord.Net.WebSocket")] -[assembly: InternalsVisibleTo("Discord.Net.Commands")] -[assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index a375d3255..643bac7bc 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,4 +1,4 @@ - + A Discord.Net extension adding support for bot commands. 1.0.0-beta2 @@ -16,7 +16,6 @@ - diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index ccd96ed08..4b8e1727d 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -10,7 +10,7 @@ namespace Discord.Commands { public T Context { get; private set; } - protected virtual async Task ReplyAsync(string message, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + protected virtual async Task ReplyAsync(string message, bool isTTS = false, Embed embed = null, RequestOptions options = null) { return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); } diff --git a/src/Discord.Net.Core/Audio/AudioInStream.cs b/src/Discord.Net.Core/Audio/AudioInStream.cs new file mode 100644 index 000000000..e6c9c4b04 --- /dev/null +++ b/src/Discord.Net.Core/Audio/AudioInStream.cs @@ -0,0 +1,8 @@ +using System.IO; + +namespace Discord.Audio +{ + public abstract class AudioInStream : Stream + { + } +} diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 5ba8b7c0c..238f3e4e2 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -15,9 +15,6 @@ - - - diff --git a/src/Discord.Net.API/Common/Direction.cs b/src/Discord.Net.Core/Entities/Channels/Direction.cs similarity index 100% rename from src/Discord.Net.API/Common/Direction.cs rename to src/Discord.Net.Core/Entities/Channels/Direction.cs diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index 893f5726a..ee47a7280 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 389a2cc27..9c9c63929 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -8,7 +8,7 @@ namespace Discord public interface IMessageChannel : IChannel { /// Sends a message to this message channel. - Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); + Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 9a89a6fa4..038faf6bc 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Threading.Tasks; namespace Discord diff --git a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs index dfcb226de..d25576c65 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using System; using System.Threading.Tasks; diff --git a/src/Discord.Net.API/Common/DefaultMessageNotifications.cs b/src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs similarity index 100% rename from src/Discord.Net.API/Common/DefaultMessageNotifications.cs rename to src/Discord.Net.Core/Entities/Guilds/DefaultMessageNotifications.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs index 94f79afa5..e925991eb 100644 --- a/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs +++ b/src/Discord.Net.Core/Entities/Guilds/GuildEmoji.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using Model = Discord.API.Emoji; namespace Discord { @@ -14,7 +12,7 @@ namespace Discord public bool RequireColons { get; } public IReadOnlyList RoleIds { get; } - private GuildEmoji(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList roleIds) + internal GuildEmoji(ulong id, string name, bool isManaged, bool requireColons, IReadOnlyList roleIds) { Id = id; Name = name; @@ -22,10 +20,6 @@ namespace Discord RequireColons = requireColons; RoleIds = roleIds; } - internal static GuildEmoji Create(Model model) - { - return new GuildEmoji(model.Id.Value, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); - } public override string ToString() => Name; private string DebuggerDisplay => $"{Name} ({Id})"; diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index a324248f5..f62ea080c 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/src/Discord.Net.API/Common/MfaLevel.cs b/src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs similarity index 100% rename from src/Discord.Net.API/Common/MfaLevel.cs rename to src/Discord.Net.Core/Entities/Guilds/MfaLevel.cs diff --git a/src/Discord.Net.API/Common/PermissionTarget.cs b/src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs similarity index 100% rename from src/Discord.Net.API/Common/PermissionTarget.cs rename to src/Discord.Net.Core/Entities/Guilds/PermissionTarget.cs diff --git a/src/Discord.Net.API/Common/VerificationLevel.cs b/src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs similarity index 100% rename from src/Discord.Net.API/Common/VerificationLevel.cs rename to src/Discord.Net.Core/Entities/Guilds/VerificationLevel.cs diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index 6e7cf1f2a..59fe8bbdb 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -1,6 +1,4 @@ using System.IO; -using Model = Discord.API.Image; - namespace Discord { /// @@ -30,10 +28,5 @@ namespace Discord Stream = File.OpenRead(path); } #endif - - public Model ToModel() - { - return new Model(Stream); - } } } diff --git a/src/Discord.Net.Core/Entities/Messages/Embed.cs b/src/Discord.Net.Core/Entities/Messages/Embed.cs new file mode 100644 index 000000000..ebde05d4c --- /dev/null +++ b/src/Discord.Net.Core/Entities/Messages/Embed.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Immutable; +using System.Diagnostics; + +namespace Discord +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class Embed : IEmbed + { + public string Type { get; } + + public string Description { get; internal set; } + public string Url { get; internal set; } + public string Title { get; internal set; } + public DateTimeOffset? Timestamp { get; internal set; } + public Color? Color { get; internal set; } + public EmbedImage? Image { get; internal set; } + public EmbedVideo? Video { get; internal set; } + public EmbedAuthor? Author { get; internal set; } + public EmbedFooter? Footer { get; internal set; } + public EmbedProvider? Provider { get; internal set; } + public EmbedThumbnail? Thumbnail { get; internal set; } + public ImmutableArray Fields { get; internal set; } + + internal Embed(string type) + { + Type = type; + Fields = ImmutableArray.Create(); + } + internal Embed(string type, + string title, + string description, + string url, + DateTimeOffset? timestamp, + Color? color, + EmbedImage? image, + EmbedVideo? video, + EmbedAuthor? author, + EmbedFooter? footer, + EmbedProvider? provider, + EmbedThumbnail? thumbnail, + ImmutableArray fields) + { + Type = type; + Title = title; + Description = description; + Url = url; + Color = color; + Timestamp = timestamp; + Image = image; + Video = video; + Author = author; + Footer = footer; + Provider = provider; + Thumbnail = thumbnail; + Fields = fields; + } + + public override string ToString() => Title; + private string DebuggerDisplay => $"{Title} ({Type})"; + } +} diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs b/src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs index b0ed0f08f..142e36832 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedAuthor.cs @@ -1,27 +1,22 @@ using System.Diagnostics; -using Model = Discord.API.EmbedAuthor; namespace Discord { [DebuggerDisplay("{DebuggerDisplay,nq}")] public struct EmbedAuthor { - public string Name { get; set; } - public string Url { get; set; } - public string IconUrl { get; set; } - public string ProxyIconUrl { get; set; } + public string Name { get; internal set; } + public string Url { get; internal set; } + public string IconUrl { get; internal set; } + public string ProxyIconUrl { get; internal set; } - private EmbedAuthor(string name, string url, string iconUrl, string proxyIconUrl) + internal EmbedAuthor(string name, string url, string iconUrl, string proxyIconUrl) { Name = name; Url = url; IconUrl = iconUrl; ProxyIconUrl = proxyIconUrl; } - internal static EmbedAuthor Create(Model model) - { - return new EmbedAuthor(model.Name, model.Url, model.IconUrl, model.ProxyIconUrl); - } private string DebuggerDisplay => $"{Name} ({Url})"; public override string ToString() => Name; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedField.cs b/src/Discord.Net.Core/Entities/Messages/EmbedField.cs index 257074e41..f7c1f8348 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedField.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedField.cs @@ -1,25 +1,20 @@ using System.Diagnostics; -using Model = Discord.API.EmbedField; namespace Discord { [DebuggerDisplay("{DebuggerDisplay,nq}")] public struct EmbedField { - public string Name { get; set; } - public string Value { get; set; } - public bool Inline { get; set; } + public string Name { get; internal set; } + public string Value { get; internal set; } + public bool Inline { get; internal set; } - private EmbedField(string name, string value, bool inline) + internal EmbedField(string name, string value, bool inline) { Name = name; Value = value; Inline = inline; } - internal static EmbedField Create(Model model) - { - return new EmbedField(model.Name, model.Value, model.Inline); - } private string DebuggerDisplay => $"{Name} ({Value}"; public override string ToString() => Name; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedFooter.cs b/src/Discord.Net.Core/Entities/Messages/EmbedFooter.cs index a69e4b077..33582070a 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedFooter.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedFooter.cs @@ -1,25 +1,20 @@ using System.Diagnostics; -using Model = Discord.API.EmbedFooter; namespace Discord { [DebuggerDisplay("{DebuggerDisplay,nq}")] public struct EmbedFooter { - public string Text { get; set; } - public string IconUrl { get; set; } - public string ProxyUrl { get; set; } + public string Text { get; internal set; } + public string IconUrl { get; internal set; } + public string ProxyUrl { get; internal set; } - private EmbedFooter(string text, string iconUrl, string proxyUrl) + internal EmbedFooter(string text, string iconUrl, string proxyUrl) { Text = text; IconUrl = iconUrl; ProxyUrl = proxyUrl; } - internal static EmbedFooter Create(Model model) - { - return new EmbedFooter(model.Text, model.IconUrl, model.ProxyIconUrl); - } private string DebuggerDisplay => $"{Text} ({IconUrl})"; public override string ToString() => Text; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs index 0b606c11e..fa4847721 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedImage.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Model = Discord.API.EmbedImage; namespace Discord { @@ -11,19 +10,13 @@ namespace Discord public int? Height { get; } public int? Width { get; } - private EmbedImage(string url, string proxyUrl, int? height, int? width) + internal EmbedImage(string url, string proxyUrl, int? height, int? width) { Url = url; ProxyUrl = proxyUrl; Height = height; Width = width; } - internal static EmbedImage Create(Model model) - { - return new EmbedImage(model.Url, model.ProxyUrl, - model.Height.IsSpecified ? model.Height.Value : (int?)null, - model.Width.IsSpecified ? model.Width.Value : (int?)null); - } private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs b/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs index 64b13e8e3..943ac5b52 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedProvider.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Model = Discord.API.EmbedProvider; namespace Discord { @@ -9,15 +8,11 @@ namespace Discord public string Name { get; } public string Url { get; } - private EmbedProvider(string name, string url) + internal EmbedProvider(string name, string url) { Name = name; Url = url; } - internal static EmbedProvider Create(Model model) - { - return new EmbedProvider(model.Name, model.Url); - } private string DebuggerDisplay => $"{Name} ({Url})"; public override string ToString() => Name; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs index dac2f5c7c..4e125bf2a 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Model = Discord.API.EmbedThumbnail; namespace Discord { @@ -11,19 +10,13 @@ namespace Discord public int? Height { get; } public int? Width { get; } - private EmbedThumbnail(string url, string proxyUrl, int? height, int? width) + internal EmbedThumbnail(string url, string proxyUrl, int? height, int? width) { Url = url; ProxyUrl = proxyUrl; Height = height; Width = width; } - internal static EmbedThumbnail Create(Model model) - { - return new EmbedThumbnail(model.Url, model.ProxyUrl, - model.Height.IsSpecified ? model.Height.Value : (int?)null, - model.Width.IsSpecified ? model.Width.Value : (int?)null); - } private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs index 3c93416d0..eaf6f4a4c 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Model = Discord.API.EmbedVideo; namespace Discord { @@ -10,18 +9,12 @@ namespace Discord public int? Height { get; } public int? Width { get; } - private EmbedVideo(string url, int? height, int? width) + internal EmbedVideo(string url, int? height, int? width) { Url = url; Height = height; Width = width; } - internal static EmbedVideo Create(Model model) - { - return new EmbedVideo(model.Url, - model.Height.IsSpecified ? model.Height.Value : (int?)null, - model.Width.IsSpecified ? model.Width.Value : (int?)null); - } private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; public override string ToString() => Url; diff --git a/src/Discord.Net.Core/Entities/Messages/Emoji.cs b/src/Discord.Net.Core/Entities/Messages/Emoji.cs index 6faa3e1a4..f0a0489e2 100644 --- a/src/Discord.Net.Core/Entities/Messages/Emoji.cs +++ b/src/Discord.Net.Core/Entities/Messages/Emoji.cs @@ -1,28 +1,22 @@ -using Discord.API; -using System; +using System; using System.Diagnostics; using System.Globalization; -using Model = Discord.API.Emoji; namespace Discord { [DebuggerDisplay("{DebuggerDisplay,nq}")] public struct Emoji { - public ulong Id { get; } + public ulong? Id { get; } public string Name { get; } - public string Url => CDN.GetEmojiUrl(Id); + public string Url => Id != null ? CDN.GetEmojiUrl(Id.Value) : null; - internal Emoji(ulong id, string name) + internal Emoji(ulong? id, string name) { Id = id; Name = name; } - internal static Emoji Create(Model emoji) - { - return new Emoji(emoji.Id.GetValueOrDefault(), emoji.Name); - } public static Emoji Parse(string text) { diff --git a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs index df8c51dea..b3f3a9c89 100644 --- a/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs +++ b/src/Discord.Net.Core/Entities/Messages/MessageProperties.cs @@ -32,6 +32,6 @@ /// /// The embed the message should display /// - public Optional Embed { get; set; } + public Optional Embed { get; set; } } } diff --git a/src/Discord.Net.API/Common/MessageType.cs b/src/Discord.Net.Core/Entities/Messages/MessageType.cs similarity index 100% rename from src/Discord.Net.API/Common/MessageType.cs rename to src/Discord.Net.Core/Entities/Messages/MessageType.cs diff --git a/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs b/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs index ff5b00623..bda67a870 100644 --- a/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs +++ b/src/Discord.Net.Core/Entities/Permissions/Overwrite.cs @@ -1,6 +1,4 @@ -using Model = Discord.API.Overwrite; - -namespace Discord +namespace Discord { public struct Overwrite { @@ -18,8 +16,5 @@ namespace Discord TargetType = targetType; Permissions = permissions; } - - public Overwrite(Model model) - : this(model.TargetId, model.TargetType, new OverwritePermissions(model.Allow, model.Deny)) { } } } diff --git a/src/Discord.Net.Core/Entities/Roles/IRole.cs b/src/Discord.Net.Core/Entities/Roles/IRole.cs index 54cc5d1ee..c40e0d716 100644 --- a/src/Discord.Net.Core/Entities/Roles/IRole.cs +++ b/src/Discord.Net.Core/Entities/Roles/IRole.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Threading.Tasks; namespace Discord diff --git a/src/Discord.Net.Core/Entities/Users/Game.cs b/src/Discord.Net.Core/Entities/Users/Game.cs index 5bed84ddb..3405b0dd4 100644 --- a/src/Discord.Net.Core/Entities/Users/Game.cs +++ b/src/Discord.Net.Core/Entities/Users/Game.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using Model = Discord.API.Game; namespace Discord { @@ -18,12 +17,6 @@ namespace Discord } private Game(string name) : this(name, null, StreamType.NotStreaming) { } - internal static Game Create(Model model) - { - return new Game(model.Name, - model.StreamUrl.GetValueOrDefault(null), - model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming); - } public override string ToString() => Name; private string DebuggerDisplay => StreamUrl != null ? $"{Name} ({StreamUrl})" : Name; diff --git a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs index d31c4fefb..79e8f5dcc 100644 --- a/src/Discord.Net.Core/Entities/Users/IGuildUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IGuildUser.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/src/Discord.Net.API/Common/StreamType.cs b/src/Discord.Net.Core/Entities/Users/StreamType.cs similarity index 100% rename from src/Discord.Net.API/Common/StreamType.cs rename to src/Discord.Net.Core/Entities/Users/StreamType.cs diff --git a/src/Discord.Net.API/Common/UserStatus.cs b/src/Discord.Net.Core/Entities/Users/UserStatus.cs similarity index 100% rename from src/Discord.Net.API/Common/UserStatus.cs rename to src/Discord.Net.Core/Entities/Users/UserStatus.cs diff --git a/src/Discord.Net.API/Optional.cs b/src/Discord.Net.Core/Utils/Optional.cs similarity index 100% rename from src/Discord.Net.API/Optional.cs rename to src/Discord.Net.Core/Utils/Optional.cs diff --git a/src/Discord.Net.API/Common/Application.cs b/src/Discord.Net.Rest/API/Common/Application.cs similarity index 95% rename from src/Discord.Net.API/Common/Application.cs rename to src/Discord.Net.Rest/API/Common/Application.cs index e72c6ce79..ca4c443f1 100644 --- a/src/Discord.Net.API/Common/Application.cs +++ b/src/Discord.Net.Rest/API/Common/Application.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Application + internal class Application { [JsonProperty("description")] public string Description { get; set; } diff --git a/src/Discord.Net.API/Common/Attachment.cs b/src/Discord.Net.Rest/API/Common/Attachment.cs similarity index 95% rename from src/Discord.Net.API/Common/Attachment.cs rename to src/Discord.Net.Rest/API/Common/Attachment.cs index 9ab2a798b..4a651d9fa 100644 --- a/src/Discord.Net.API/Common/Attachment.cs +++ b/src/Discord.Net.Rest/API/Common/Attachment.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Attachment + internal class Attachment { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/Ban.cs b/src/Discord.Net.Rest/API/Common/Ban.cs similarity index 91% rename from src/Discord.Net.API/Common/Ban.cs rename to src/Discord.Net.Rest/API/Common/Ban.cs index c1f667e65..202004f53 100644 --- a/src/Discord.Net.API/Common/Ban.cs +++ b/src/Discord.Net.Rest/API/Common/Ban.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Ban + internal class Ban { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Common/Channel.cs b/src/Discord.Net.Rest/API/Common/Channel.cs similarity index 98% rename from src/Discord.Net.API/Common/Channel.cs rename to src/Discord.Net.Rest/API/Common/Channel.cs index 5b02e6806..56a24a1f4 100644 --- a/src/Discord.Net.API/Common/Channel.cs +++ b/src/Discord.Net.Rest/API/Common/Channel.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API { - public class Channel + internal class Channel { //Shared [JsonProperty("id")] diff --git a/src/Discord.Net.API/Common/Connection.cs b/src/Discord.Net.Rest/API/Common/Connection.cs similarity index 94% rename from src/Discord.Net.API/Common/Connection.cs rename to src/Discord.Net.Rest/API/Common/Connection.cs index 0d218d6a2..ad0a76ac1 100644 --- a/src/Discord.Net.API/Common/Connection.cs +++ b/src/Discord.Net.Rest/API/Common/Connection.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Discord.API { - public class Connection + internal class Connection { [JsonProperty("id")] public string Id { get; set; } diff --git a/src/Discord.Net.API/Common/Embed.cs b/src/Discord.Net.Rest/API/Common/Embed.cs similarity index 97% rename from src/Discord.Net.API/Common/Embed.cs rename to src/Discord.Net.Rest/API/Common/Embed.cs index 33b97e641..f6325efbb 100644 --- a/src/Discord.Net.API/Common/Embed.cs +++ b/src/Discord.Net.Rest/API/Common/Embed.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Embed + internal class Embed { [JsonProperty("title")] public string Title { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedAuthor.cs b/src/Discord.Net.Rest/API/Common/EmbedAuthor.cs similarity index 92% rename from src/Discord.Net.API/Common/EmbedAuthor.cs rename to src/Discord.Net.Rest/API/Common/EmbedAuthor.cs index 973f7d5ea..e69fee6eb 100644 --- a/src/Discord.Net.API/Common/EmbedAuthor.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedAuthor.cs @@ -2,7 +2,7 @@ namespace Discord.API { - public class EmbedAuthor + internal class EmbedAuthor { [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedField.cs b/src/Discord.Net.Rest/API/Common/EmbedField.cs similarity index 90% rename from src/Discord.Net.API/Common/EmbedField.cs rename to src/Discord.Net.Rest/API/Common/EmbedField.cs index 12aa0137a..6ce810f1a 100644 --- a/src/Discord.Net.API/Common/EmbedField.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedField.cs @@ -2,7 +2,7 @@ namespace Discord.API { - public class EmbedField + internal class EmbedField { [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedFooter.cs b/src/Discord.Net.Rest/API/Common/EmbedFooter.cs similarity index 90% rename from src/Discord.Net.API/Common/EmbedFooter.cs rename to src/Discord.Net.Rest/API/Common/EmbedFooter.cs index 2ad22cae7..27048972e 100644 --- a/src/Discord.Net.API/Common/EmbedFooter.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedFooter.cs @@ -2,7 +2,7 @@ namespace Discord.API { - public class EmbedFooter + internal class EmbedFooter { [JsonProperty("text")] public string Text { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedImage.cs b/src/Discord.Net.Rest/API/Common/EmbedImage.cs similarity index 93% rename from src/Discord.Net.API/Common/EmbedImage.cs rename to src/Discord.Net.Rest/API/Common/EmbedImage.cs index ab4941ae0..a5ef748f8 100644 --- a/src/Discord.Net.API/Common/EmbedImage.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedImage.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class EmbedImage + internal class EmbedImage { [JsonProperty("url")] public string Url { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedProvider.cs b/src/Discord.Net.Rest/API/Common/EmbedProvider.cs similarity index 87% rename from src/Discord.Net.API/Common/EmbedProvider.cs rename to src/Discord.Net.Rest/API/Common/EmbedProvider.cs index c23998628..8c46b10dc 100644 --- a/src/Discord.Net.API/Common/EmbedProvider.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedProvider.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class EmbedProvider + internal class EmbedProvider { [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedThumbnail.cs b/src/Discord.Net.Rest/API/Common/EmbedThumbnail.cs similarity index 92% rename from src/Discord.Net.API/Common/EmbedThumbnail.cs rename to src/Discord.Net.Rest/API/Common/EmbedThumbnail.cs index 59cbd9e6a..f22953a25 100644 --- a/src/Discord.Net.API/Common/EmbedThumbnail.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedThumbnail.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class EmbedThumbnail + internal class EmbedThumbnail { [JsonProperty("url")] public string Url { get; set; } diff --git a/src/Discord.Net.API/Common/EmbedVideo.cs b/src/Discord.Net.Rest/API/Common/EmbedVideo.cs similarity index 91% rename from src/Discord.Net.API/Common/EmbedVideo.cs rename to src/Discord.Net.Rest/API/Common/EmbedVideo.cs index 072004631..09e933784 100644 --- a/src/Discord.Net.API/Common/EmbedVideo.cs +++ b/src/Discord.Net.Rest/API/Common/EmbedVideo.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class EmbedVideo + internal class EmbedVideo { [JsonProperty("url")] public string Url { get; set; } diff --git a/src/Discord.Net.API/Common/Emoji.cs b/src/Discord.Net.Rest/API/Common/Emoji.cs similarity index 95% rename from src/Discord.Net.API/Common/Emoji.cs rename to src/Discord.Net.Rest/API/Common/Emoji.cs index c04786039..bd9c4d466 100644 --- a/src/Discord.Net.API/Common/Emoji.cs +++ b/src/Discord.Net.Rest/API/Common/Emoji.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Emoji + internal class Emoji { [JsonProperty("id")] public ulong? Id { get; set; } diff --git a/src/Discord.Net.API/Common/Game.cs b/src/Discord.Net.Rest/API/Common/Game.cs similarity index 96% rename from src/Discord.Net.API/Common/Game.cs rename to src/Discord.Net.Rest/API/Common/Game.cs index df5c78880..a499d83b0 100644 --- a/src/Discord.Net.API/Common/Game.cs +++ b/src/Discord.Net.Rest/API/Common/Game.cs @@ -5,7 +5,7 @@ using System.Runtime.Serialization; namespace Discord.API { - public class Game + internal class Game { [JsonProperty("name")] public string Name { get; set; } diff --git a/src/Discord.Net.API/Common/Guild.cs b/src/Discord.Net.Rest/API/Common/Guild.cs similarity index 98% rename from src/Discord.Net.API/Common/Guild.cs rename to src/Discord.Net.Rest/API/Common/Guild.cs index 567b34714..b69ba1293 100644 --- a/src/Discord.Net.API/Common/Guild.cs +++ b/src/Discord.Net.Rest/API/Common/Guild.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Guild + internal class Guild { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/GuildEmbed.cs b/src/Discord.Net.Rest/API/Common/GuildEmbed.cs similarity index 89% rename from src/Discord.Net.API/Common/GuildEmbed.cs rename to src/Discord.Net.Rest/API/Common/GuildEmbed.cs index 3cf4176eb..ff8b8e180 100644 --- a/src/Discord.Net.API/Common/GuildEmbed.cs +++ b/src/Discord.Net.Rest/API/Common/GuildEmbed.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class GuildEmbed + internal class GuildEmbed { [JsonProperty("enabled")] public bool Enabled { get; set; } diff --git a/src/Discord.Net.API/Common/GuildMember.cs b/src/Discord.Net.Rest/API/Common/GuildMember.cs similarity index 94% rename from src/Discord.Net.API/Common/GuildMember.cs rename to src/Discord.Net.Rest/API/Common/GuildMember.cs index 116a2b29a..ba3d05462 100644 --- a/src/Discord.Net.API/Common/GuildMember.cs +++ b/src/Discord.Net.Rest/API/Common/GuildMember.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API { - public class GuildMember + internal class GuildMember { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Common/Integration.cs b/src/Discord.Net.Rest/API/Common/Integration.cs similarity index 96% rename from src/Discord.Net.API/Common/Integration.cs rename to src/Discord.Net.Rest/API/Common/Integration.cs index c27649f97..821359975 100644 --- a/src/Discord.Net.API/Common/Integration.cs +++ b/src/Discord.Net.Rest/API/Common/Integration.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API { - public class Integration + internal class Integration { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/IntegrationAccount.cs b/src/Discord.Net.Rest/API/Common/IntegrationAccount.cs similarity index 86% rename from src/Discord.Net.API/Common/IntegrationAccount.cs rename to src/Discord.Net.Rest/API/Common/IntegrationAccount.cs index d1ab761b6..22831e795 100644 --- a/src/Discord.Net.API/Common/IntegrationAccount.cs +++ b/src/Discord.Net.Rest/API/Common/IntegrationAccount.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class IntegrationAccount + internal class IntegrationAccount { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/Invite.cs b/src/Discord.Net.Rest/API/Common/Invite.cs similarity index 92% rename from src/Discord.Net.API/Common/Invite.cs rename to src/Discord.Net.Rest/API/Common/Invite.cs index 646e30a59..67a318c5a 100644 --- a/src/Discord.Net.API/Common/Invite.cs +++ b/src/Discord.Net.Rest/API/Common/Invite.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Invite + internal class Invite { [JsonProperty("code")] public string Code { get; set; } diff --git a/src/Discord.Net.API/Common/InviteChannel.cs b/src/Discord.Net.Rest/API/Common/InviteChannel.cs similarity index 90% rename from src/Discord.Net.API/Common/InviteChannel.cs rename to src/Discord.Net.Rest/API/Common/InviteChannel.cs index 4780573b3..ca9699067 100644 --- a/src/Discord.Net.API/Common/InviteChannel.cs +++ b/src/Discord.Net.Rest/API/Common/InviteChannel.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class InviteChannel + internal class InviteChannel { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/InviteGuild.cs b/src/Discord.Net.Rest/API/Common/InviteGuild.cs similarity index 91% rename from src/Discord.Net.API/Common/InviteGuild.cs rename to src/Discord.Net.Rest/API/Common/InviteGuild.cs index f358625b3..3d6d7cd74 100644 --- a/src/Discord.Net.API/Common/InviteGuild.cs +++ b/src/Discord.Net.Rest/API/Common/InviteGuild.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class InviteGuild + internal class InviteGuild { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/InviteMetadata.cs b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs similarity index 93% rename from src/Discord.Net.API/Common/InviteMetadata.cs rename to src/Discord.Net.Rest/API/Common/InviteMetadata.cs index a9603869d..586307523 100644 --- a/src/Discord.Net.API/Common/InviteMetadata.cs +++ b/src/Discord.Net.Rest/API/Common/InviteMetadata.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API { - public class InviteMetadata : Invite + internal class InviteMetadata : Invite { [JsonProperty("inviter")] public User Inviter { get; set; } diff --git a/src/Discord.Net.API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs similarity index 98% rename from src/Discord.Net.API/Common/Message.cs rename to src/Discord.Net.Rest/API/Common/Message.cs index f812d0622..9a7629b96 100644 --- a/src/Discord.Net.API/Common/Message.cs +++ b/src/Discord.Net.Rest/API/Common/Message.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API { - public class Message + internal class Message { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/Overwrite.cs b/src/Discord.Net.Rest/API/Common/Overwrite.cs similarity index 93% rename from src/Discord.Net.API/Common/Overwrite.cs rename to src/Discord.Net.Rest/API/Common/Overwrite.cs index c72e03395..1ba836127 100644 --- a/src/Discord.Net.API/Common/Overwrite.cs +++ b/src/Discord.Net.Rest/API/Common/Overwrite.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Overwrite + internal class Overwrite { [JsonProperty("id")] public ulong TargetId { get; set; } diff --git a/src/Discord.Net.API/Common/Presence.cs b/src/Discord.Net.Rest/API/Common/Presence.cs similarity index 95% rename from src/Discord.Net.API/Common/Presence.cs rename to src/Discord.Net.Rest/API/Common/Presence.cs index 86ccd60bc..2902b7ce3 100644 --- a/src/Discord.Net.API/Common/Presence.cs +++ b/src/Discord.Net.Rest/API/Common/Presence.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Presence + internal class Presence { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Common/Reaction.cs b/src/Discord.Net.Rest/API/Common/Reaction.cs similarity index 62% rename from src/Discord.Net.API/Common/Reaction.cs rename to src/Discord.Net.Rest/API/Common/Reaction.cs index e143004ef..4d368ab2d 100644 --- a/src/Discord.Net.API/Common/Reaction.cs +++ b/src/Discord.Net.Rest/API/Common/Reaction.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Newtonsoft.Json; +using Newtonsoft.Json; namespace Discord.API { - public class Reaction + internal class Reaction { [JsonProperty("count")] public int Count { get; set; } diff --git a/src/Discord.Net.API/Common/ReadState.cs b/src/Discord.Net.Rest/API/Common/ReadState.cs similarity index 92% rename from src/Discord.Net.API/Common/ReadState.cs rename to src/Discord.Net.Rest/API/Common/ReadState.cs index 7e1cacf56..6ea6e4bd0 100644 --- a/src/Discord.Net.API/Common/ReadState.cs +++ b/src/Discord.Net.Rest/API/Common/ReadState.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class ReadState + internal class ReadState { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/Relationship.cs b/src/Discord.Net.Rest/API/Common/Relationship.cs similarity index 90% rename from src/Discord.Net.API/Common/Relationship.cs rename to src/Discord.Net.Rest/API/Common/Relationship.cs index 877d0cf2a..ecbb96f80 100644 --- a/src/Discord.Net.API/Common/Relationship.cs +++ b/src/Discord.Net.Rest/API/Common/Relationship.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Relationship + internal class Relationship { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/RelationshipType.cs b/src/Discord.Net.Rest/API/Common/RelationshipType.cs similarity index 82% rename from src/Discord.Net.API/Common/RelationshipType.cs rename to src/Discord.Net.Rest/API/Common/RelationshipType.cs index 94f0f73b4..0ed99f396 100644 --- a/src/Discord.Net.API/Common/RelationshipType.cs +++ b/src/Discord.Net.Rest/API/Common/RelationshipType.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API { - public enum RelationshipType + internal enum RelationshipType { Friend = 1, Blocked = 2, diff --git a/src/Discord.Net.API/Common/Role.cs b/src/Discord.Net.Rest/API/Common/Role.cs similarity index 96% rename from src/Discord.Net.API/Common/Role.cs rename to src/Discord.Net.Rest/API/Common/Role.cs index 6a3659489..856a8695f 100644 --- a/src/Discord.Net.API/Common/Role.cs +++ b/src/Discord.Net.Rest/API/Common/Role.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class Role + internal class Role { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/User.cs b/src/Discord.Net.Rest/API/Common/User.cs similarity index 97% rename from src/Discord.Net.API/Common/User.cs rename to src/Discord.Net.Rest/API/Common/User.cs index e8674a95c..d49d24623 100644 --- a/src/Discord.Net.API/Common/User.cs +++ b/src/Discord.Net.Rest/API/Common/User.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class User + internal class User { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/UserGuild.cs b/src/Discord.Net.Rest/API/Common/UserGuild.cs similarity index 94% rename from src/Discord.Net.API/Common/UserGuild.cs rename to src/Discord.Net.Rest/API/Common/UserGuild.cs index 7d7905dae..f4f763885 100644 --- a/src/Discord.Net.API/Common/UserGuild.cs +++ b/src/Discord.Net.Rest/API/Common/UserGuild.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class UserGuild + internal class UserGuild { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Common/VoiceRegion.cs b/src/Discord.Net.Rest/API/Common/VoiceRegion.cs similarity index 94% rename from src/Discord.Net.API/Common/VoiceRegion.cs rename to src/Discord.Net.Rest/API/Common/VoiceRegion.cs index aef07cc78..5f31e8f64 100644 --- a/src/Discord.Net.API/Common/VoiceRegion.cs +++ b/src/Discord.Net.Rest/API/Common/VoiceRegion.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class VoiceRegion + internal class VoiceRegion { [JsonProperty("id")] public string Id { get; set; } diff --git a/src/Discord.Net.API/Common/VoiceState.cs b/src/Discord.Net.Rest/API/Common/VoiceState.cs similarity index 96% rename from src/Discord.Net.API/Common/VoiceState.cs rename to src/Discord.Net.Rest/API/Common/VoiceState.cs index 2039f25f5..563a5f95b 100644 --- a/src/Discord.Net.API/Common/VoiceState.cs +++ b/src/Discord.Net.Rest/API/Common/VoiceState.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class VoiceState + internal class VoiceState { [JsonProperty("guild_id")] public ulong? GuildId { get; set; } diff --git a/src/Discord.Net.API/EntityOrId.cs b/src/Discord.Net.Rest/API/EntityOrId.cs similarity index 90% rename from src/Discord.Net.API/EntityOrId.cs rename to src/Discord.Net.Rest/API/EntityOrId.cs index 01ccdcb22..9bcda260a 100644 --- a/src/Discord.Net.API/EntityOrId.cs +++ b/src/Discord.Net.Rest/API/EntityOrId.cs @@ -1,6 +1,6 @@ namespace Discord.API { - public struct EntityOrId + internal struct EntityOrId { public ulong Id { get; } public T Object { get; } diff --git a/src/Discord.Net.API/Image.cs b/src/Discord.Net.Rest/API/Image.cs similarity index 93% rename from src/Discord.Net.API/Image.cs rename to src/Discord.Net.Rest/API/Image.cs index 5442bd30f..b2357a0a6 100644 --- a/src/Discord.Net.API/Image.cs +++ b/src/Discord.Net.Rest/API/Image.cs @@ -2,7 +2,7 @@ namespace Discord.API { - public struct Image + internal struct Image { public Stream Stream { get; } public string Hash { get; } diff --git a/src/Discord.Net.API/Int53Attribute.cs b/src/Discord.Net.Rest/API/Int53Attribute.cs similarity index 71% rename from src/Discord.Net.API/Int53Attribute.cs rename to src/Discord.Net.Rest/API/Int53Attribute.cs index 3e9139b9d..70ef2f185 100644 --- a/src/Discord.Net.API/Int53Attribute.cs +++ b/src/Discord.Net.Rest/API/Int53Attribute.cs @@ -4,5 +4,5 @@ using System; namespace Discord.API { [AttributeUsage(AttributeTargets.Property)] - public class Int53Attribute : Attribute { } + internal class Int53Attribute : Attribute { } } diff --git a/src/Discord.Net.API/Net/MultipartFile.cs b/src/Discord.Net.Rest/API/Net/MultipartFile.cs similarity index 89% rename from src/Discord.Net.API/Net/MultipartFile.cs rename to src/Discord.Net.Rest/API/Net/MultipartFile.cs index f7244afd7..604852e90 100644 --- a/src/Discord.Net.API/Net/MultipartFile.cs +++ b/src/Discord.Net.Rest/API/Net/MultipartFile.cs @@ -2,7 +2,7 @@ namespace Discord.Net.Rest { - struct MultipartFile + internal struct MultipartFile { public Stream Stream { get; } public string Filename { get; } diff --git a/src/Discord.Net.API/Rest/CreateChannelInviteParams.cs b/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs similarity index 90% rename from src/Discord.Net.API/Rest/CreateChannelInviteParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs index 8a619a8b7..cc3ca6dca 100644 --- a/src/Discord.Net.API/Rest/CreateChannelInviteParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateChannelInviteParams + internal class CreateChannelInviteParams { [JsonProperty("max_age")] public Optional MaxAge { get; set; } diff --git a/src/Discord.Net.API/Rest/CreateDMChannelParams.cs b/src/Discord.Net.Rest/API/Rest/CreateDMChannelParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/CreateDMChannelParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateDMChannelParams.cs index 83fe76e98..f32796e02 100644 --- a/src/Discord.Net.API/Rest/CreateDMChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateDMChannelParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateDMChannelParams + internal class CreateDMChannelParams { [JsonProperty("recipient_id")] public ulong RecipientId { get; } diff --git a/src/Discord.Net.API/Rest/CreateGuildBanParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildBanParams.cs similarity index 77% rename from src/Discord.Net.API/Rest/CreateGuildBanParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateGuildBanParams.cs index 724112bc0..0c148fe70 100644 --- a/src/Discord.Net.API/Rest/CreateGuildBanParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildBanParams.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API.Rest { - public class CreateGuildBanParams + internal class CreateGuildBanParams { public Optional DeleteMessageDays { get; set; } } diff --git a/src/Discord.Net.API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs similarity index 92% rename from src/Discord.Net.API/Rest/CreateGuildChannelParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs index f0e06e3d2..bae677148 100644 --- a/src/Discord.Net.API/Rest/CreateGuildChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateGuildChannelParams + internal class CreateGuildChannelParams { [JsonProperty("name")] public string Name { get; } diff --git a/src/Discord.Net.API/Rest/CreateGuildIntegrationParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildIntegrationParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/CreateGuildIntegrationParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateGuildIntegrationParams.cs index 0d6e3a654..1053a0ed3 100644 --- a/src/Discord.Net.API/Rest/CreateGuildIntegrationParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildIntegrationParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateGuildIntegrationParams + internal class CreateGuildIntegrationParams { [JsonProperty("id")] public ulong Id { get; } diff --git a/src/Discord.Net.API/Rest/CreateGuildParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildParams.cs similarity index 93% rename from src/Discord.Net.API/Rest/CreateGuildParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateGuildParams.cs index 4bc18c28b..cda6caedf 100644 --- a/src/Discord.Net.API/Rest/CreateGuildParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateGuildParams + internal class CreateGuildParams { [JsonProperty("name")] public string Name { get; } diff --git a/src/Discord.Net.API/Rest/CreateMessageParams.cs b/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs similarity index 92% rename from src/Discord.Net.API/Rest/CreateMessageParams.cs rename to src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs index a0dbb59dd..d77bff8ca 100644 --- a/src/Discord.Net.API/Rest/CreateMessageParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateMessageParams.cs @@ -1,11 +1,10 @@ #pragma warning disable CS1591 -using System; using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class CreateMessageParams + internal class CreateMessageParams { [JsonProperty("content")] public string Content { get; } diff --git a/src/Discord.Net.API/Rest/DeleteMessagesParams.cs b/src/Discord.Net.Rest/API/Rest/DeleteMessagesParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/DeleteMessagesParams.cs rename to src/Discord.Net.Rest/API/Rest/DeleteMessagesParams.cs index 09b9a2bf1..ca9d8c26e 100644 --- a/src/Discord.Net.API/Rest/DeleteMessagesParams.cs +++ b/src/Discord.Net.Rest/API/Rest/DeleteMessagesParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class DeleteMessagesParams + internal class DeleteMessagesParams { [JsonProperty("messages")] public ulong[] MessageIds { get; } diff --git a/src/Discord.Net.API/Rest/GetBotGatewayResponse.cs b/src/Discord.Net.Rest/API/Rest/GetBotGatewayResponse.cs similarity index 85% rename from src/Discord.Net.API/Rest/GetBotGatewayResponse.cs rename to src/Discord.Net.Rest/API/Rest/GetBotGatewayResponse.cs index d6d2bed00..111fcf3db 100644 --- a/src/Discord.Net.API/Rest/GetBotGatewayResponse.cs +++ b/src/Discord.Net.Rest/API/Rest/GetBotGatewayResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { - public class GetBotGatewayResponse + internal class GetBotGatewayResponse { [JsonProperty("url")] public string Url { get; set; } diff --git a/src/Discord.Net.API/Rest/GetChannelMessagesParams.cs b/src/Discord.Net.Rest/API/Rest/GetChannelMessagesParams.cs similarity index 85% rename from src/Discord.Net.API/Rest/GetChannelMessagesParams.cs rename to src/Discord.Net.Rest/API/Rest/GetChannelMessagesParams.cs index 2d00833ca..ea5327667 100644 --- a/src/Discord.Net.API/Rest/GetChannelMessagesParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetChannelMessagesParams.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API.Rest { - public class GetChannelMessagesParams + internal class GetChannelMessagesParams { public Optional Limit { get; set; } public Optional RelativeDirection { get; set; } diff --git a/src/Discord.Net.API/Rest/GetGatewayResponse.cs b/src/Discord.Net.Rest/API/Rest/GetGatewayResponse.cs similarity index 81% rename from src/Discord.Net.API/Rest/GetGatewayResponse.cs rename to src/Discord.Net.Rest/API/Rest/GetGatewayResponse.cs index 1ca03b73b..ce3630170 100644 --- a/src/Discord.Net.API/Rest/GetGatewayResponse.cs +++ b/src/Discord.Net.Rest/API/Rest/GetGatewayResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { - public class GetGatewayResponse + internal class GetGatewayResponse { [JsonProperty("url")] public string Url { get; set; } diff --git a/src/Discord.Net.API/Rest/GetGuildMembersParams.cs b/src/Discord.Net.Rest/API/Rest/GetGuildMembersParams.cs similarity index 81% rename from src/Discord.Net.API/Rest/GetGuildMembersParams.cs rename to src/Discord.Net.Rest/API/Rest/GetGuildMembersParams.cs index 2bd34ddcb..66023cb43 100644 --- a/src/Discord.Net.API/Rest/GetGuildMembersParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetGuildMembersParams.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API.Rest { - public class GetGuildMembersParams + internal class GetGuildMembersParams { public Optional Limit { get; set; } public Optional AfterUserId { get; set; } diff --git a/src/Discord.Net.API/Rest/GetGuildPruneCountResponse.cs b/src/Discord.Net.Rest/API/Rest/GetGuildPruneCountResponse.cs similarity index 79% rename from src/Discord.Net.API/Rest/GetGuildPruneCountResponse.cs rename to src/Discord.Net.Rest/API/Rest/GetGuildPruneCountResponse.cs index 3f832caee..4af85acfa 100644 --- a/src/Discord.Net.API/Rest/GetGuildPruneCountResponse.cs +++ b/src/Discord.Net.Rest/API/Rest/GetGuildPruneCountResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { - public class GetGuildPruneCountResponse + internal class GetGuildPruneCountResponse { [JsonProperty("pruned")] public int Pruned { get; set; } diff --git a/src/Discord.Net.API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs similarity index 78% rename from src/Discord.Net.API/Rest/GetReactionUsersParams.cs rename to src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs index bb9b22ab8..d70da5632 100644 --- a/src/Discord.Net.API/Rest/GetReactionUsersParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs @@ -1,6 +1,6 @@ namespace Discord.API.Rest { - public class GetReactionUsersParams + internal class GetReactionUsersParams { public Optional Limit { get; set; } public Optional AfterUserId { get; set; } diff --git a/src/Discord.Net.API/Rest/GuildPruneParams.cs b/src/Discord.Net.Rest/API/Rest/GuildPruneParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/GuildPruneParams.cs rename to src/Discord.Net.Rest/API/Rest/GuildPruneParams.cs index 9cff46992..6a98d3758 100644 --- a/src/Discord.Net.API/Rest/GuildPruneParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GuildPruneParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class GuildPruneParams + internal class GuildPruneParams { [JsonProperty("days")] public int Days { get; } diff --git a/src/Discord.Net.API/Rest/ModifyChannelPermissionsParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs similarity index 91% rename from src/Discord.Net.API/Rest/ModifyChannelPermissionsParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs index 8676b22e7..0fe5f7e5a 100644 --- a/src/Discord.Net.API/Rest/ModifyChannelPermissionsParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyChannelPermissionsParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyChannelPermissionsParams + internal class ModifyChannelPermissionsParams { [JsonProperty("type")] public string Type { get; } diff --git a/src/Discord.Net.API/Rest/ModifyCurrentUserNickParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyCurrentUserNickParams.cs similarity index 88% rename from src/Discord.Net.API/Rest/ModifyCurrentUserNickParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyCurrentUserNickParams.cs index ca7ad2bd3..ba44e34cf 100644 --- a/src/Discord.Net.API/Rest/ModifyCurrentUserNickParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyCurrentUserNickParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyCurrentUserNickParams + internal class ModifyCurrentUserNickParams { [JsonProperty("nick")] public string Nickname { get; } diff --git a/src/Discord.Net.API/Rest/ModifyCurrentUserParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyCurrentUserParams.cs similarity index 88% rename from src/Discord.Net.API/Rest/ModifyCurrentUserParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyCurrentUserParams.cs index 3f7afe187..7ba27c3a5 100644 --- a/src/Discord.Net.API/Rest/ModifyCurrentUserParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyCurrentUserParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyCurrentUserParams + internal class ModifyCurrentUserParams { [JsonProperty("username")] public Optional Username { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs similarity index 88% rename from src/Discord.Net.API/Rest/ModifyGuildChannelParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs index 6d6ee4c24..b4add2ac9 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildChannelParams + internal class ModifyGuildChannelParams { [JsonProperty("name")] public Optional Name { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildChannelsParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelsParams.cs similarity index 90% rename from src/Discord.Net.API/Rest/ModifyGuildChannelsParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildChannelsParams.cs index 8ac3299fa..2bbb58ea6 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildChannelsParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildChannelsParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildChannelsParams + internal class ModifyGuildChannelsParams { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildEmbedParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmbedParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/ModifyGuildEmbedParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildEmbedParams.cs index f362f8cd7..487744c65 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildEmbedParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildEmbedParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildEmbedParams + internal class ModifyGuildEmbedParams { [JsonProperty("enabled")] public Optional Enabled { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildIntegrationParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildIntegrationParams.cs similarity index 90% rename from src/Discord.Net.API/Rest/ModifyGuildIntegrationParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildIntegrationParams.cs index 3a5526c96..0a1b4f9fa 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildIntegrationParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildIntegrationParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildIntegrationParams + internal class ModifyGuildIntegrationParams { [JsonProperty("expire_behavior")] public Optional ExpireBehavior { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildMemberParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildMemberParams.cs similarity index 93% rename from src/Discord.Net.API/Rest/ModifyGuildMemberParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildMemberParams.cs index 17a8e2da1..159670afb 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildMemberParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildMemberParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildMemberParams + internal class ModifyGuildMemberParams { [JsonProperty("mute")] public Optional Mute { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs similarity index 96% rename from src/Discord.Net.API/Rest/ModifyGuildParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs index f72ff2c96..2c7d84087 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildParams + internal class ModifyGuildParams { [JsonProperty("username")] public Optional Username { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildRoleParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs similarity index 94% rename from src/Discord.Net.API/Rest/ModifyGuildRoleParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs index f004b201e..c3c20706b 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildRoleParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildRoleParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildRoleParams + internal class ModifyGuildRoleParams { [JsonProperty("name")] public Optional Name { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyGuildRolesParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyGuildRolesParams.cs similarity index 82% rename from src/Discord.Net.API/Rest/ModifyGuildRolesParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyGuildRolesParams.cs index 2350a8c47..38c3fb646 100644 --- a/src/Discord.Net.API/Rest/ModifyGuildRolesParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyGuildRolesParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyGuildRolesParams : ModifyGuildRoleParams + internal class ModifyGuildRolesParams : ModifyGuildRoleParams { [JsonProperty("id")] public ulong Id { get; } diff --git a/src/Discord.Net.API/Rest/ModifyMessageParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs similarity index 89% rename from src/Discord.Net.API/Rest/ModifyMessageParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs index c87d82c51..fdff4de15 100644 --- a/src/Discord.Net.API/Rest/ModifyMessageParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyMessageParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyMessageParams + internal class ModifyMessageParams { [JsonProperty("content")] public Optional Content { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyTextChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyTextChannelParams.cs similarity index 78% rename from src/Discord.Net.API/Rest/ModifyTextChannelParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyTextChannelParams.cs index 3546cee95..311336ec3 100644 --- a/src/Discord.Net.API/Rest/ModifyTextChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyTextChannelParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyTextChannelParams : ModifyGuildChannelParams + internal class ModifyTextChannelParams : ModifyGuildChannelParams { [JsonProperty("topic")] public Optional Topic { get; set; } diff --git a/src/Discord.Net.API/Rest/ModifyVoiceChannelParams.cs b/src/Discord.Net.Rest/API/Rest/ModifyVoiceChannelParams.cs similarity index 82% rename from src/Discord.Net.API/Rest/ModifyVoiceChannelParams.cs rename to src/Discord.Net.Rest/API/Rest/ModifyVoiceChannelParams.cs index 8b5af9d8e..ce36eb11f 100644 --- a/src/Discord.Net.API/Rest/ModifyVoiceChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/ModifyVoiceChannelParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rest { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ModifyVoiceChannelParams : ModifyGuildChannelParams + internal class ModifyVoiceChannelParams : ModifyGuildChannelParams { [JsonProperty("bitrate")] public Optional Bitrate { get; set; } diff --git a/src/Discord.Net.API/Rest/UploadFileParams.cs b/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs similarity index 96% rename from src/Discord.Net.API/Rest/UploadFileParams.cs rename to src/Discord.Net.Rest/API/Rest/UploadFileParams.cs index bbd798900..30bfc7f9a 100644 --- a/src/Discord.Net.API/Rest/UploadFileParams.cs +++ b/src/Discord.Net.Rest/API/Rest/UploadFileParams.cs @@ -5,7 +5,7 @@ using System.IO; namespace Discord.API.Rest { - public class UploadFileParams + internal class UploadFileParams { public Stream File { get; } diff --git a/src/Discord.Net.API/RpcFrame.cs b/src/Discord.Net.Rest/API/RpcFrame.cs similarity index 94% rename from src/Discord.Net.API/RpcFrame.cs rename to src/Discord.Net.Rest/API/RpcFrame.cs index cac150e3b..523378b04 100644 --- a/src/Discord.Net.API/RpcFrame.cs +++ b/src/Discord.Net.Rest/API/RpcFrame.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API.Rpc { - public class RpcFrame + internal class RpcFrame { [JsonProperty("cmd")] public string Cmd { get; set; } diff --git a/src/Discord.Net.API/SocketFrame.cs b/src/Discord.Net.Rest/API/SocketFrame.cs similarity index 93% rename from src/Discord.Net.API/SocketFrame.cs rename to src/Discord.Net.Rest/API/SocketFrame.cs index fd9367ca4..fae741432 100644 --- a/src/Discord.Net.API/SocketFrame.cs +++ b/src/Discord.Net.Rest/API/SocketFrame.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API { - public class SocketFrame + internal class SocketFrame { [JsonProperty("op")] public int Operation { get; set; } diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index c36031539..592931c86 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -23,7 +23,7 @@ namespace Discord.Rest private bool _isFirstLogin; private bool _isDisposed; - public API.DiscordRestApiClient ApiClient { get; } + internal API.DiscordRestApiClient ApiClient { get; } internal LogManager LogManager { get; } public LoginState LoginState { get; private set; } public ISelfUser CurrentUser { get; protected set; } diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index ec4373263..8fc1eb274 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 07389f8df..66021db31 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -22,7 +22,7 @@ using System.Threading.Tasks; namespace Discord.API { - public class DiscordRestApiClient : IDisposable + internal class DiscordRestApiClient : IDisposable { private static readonly ConcurrentDictionary> _bucketIdGenerators = new ConcurrentDictionary>(); @@ -45,7 +45,9 @@ namespace Discord.API public LoginState LoginState { get; private set; } public TokenType AuthTokenType { get; private set; } - public User CurrentUser { get; private set; } + internal User CurrentUser { get; private set; } + + public ulong? CurrentUserId => CurrentUser?.Id; public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, bool fetchCurrentUser = true) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index e97a59ec5..9126ace5c 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -150,9 +150,9 @@ namespace Discord.Rest } public static async Task SendMessageAsync(IMessageChannel channel, BaseDiscordClient client, - string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + string text, bool isTTS, Embed embed, RequestOptions options) { - var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.Build() }; + var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed.ToModel() }; var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); return RestUserMessage.Create(client, channel, client.CurrentUser, model); } diff --git a/src/Discord.Net.API/Common/ChannelType.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelType.cs similarity index 100% rename from src/Discord.Net.API/Common/ChannelType.cs rename to src/Discord.Net.Rest/Entities/Channels/ChannelType.cs diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index 7fbbd21c1..2c006834c 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -7,7 +7,7 @@ namespace Discord.Rest public interface IRestMessageChannel : IMessageChannel { /// Sends a message to this message channel. - new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); + new Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 5205d8221..75b331499 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -63,7 +63,7 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -130,7 +130,7 @@ namespace Discord.Rest #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 2892695c4..a4b49b118 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -76,7 +76,7 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -140,7 +140,7 @@ namespace Discord.Rest #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index dc7b3250a..c3b14cbe6 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -45,7 +45,7 @@ namespace Discord.Rest var overwrites = model.PermissionOverwrites.Value; var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); for (int i = 0; i < overwrites.Length; i++) - newOverwrites.Add(new Overwrite(overwrites[i])); + newOverwrites.Add(overwrites[i].ToEntity()); _overwrites = newOverwrites.ToImmutable(); } @@ -83,12 +83,12 @@ namespace Discord.Rest public async Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions perms, RequestOptions options = null) { await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, user, perms, options).ConfigureAwait(false); - _overwrites = _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = user.Id, TargetType = PermissionTarget.User })); + _overwrites = _overwrites.Add(new Overwrite(user.Id, PermissionTarget.User, new OverwritePermissions(perms.AllowValue, perms.DenyValue))); } public async Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions perms, RequestOptions options = null) { await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, role, perms, options).ConfigureAwait(false); - _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = role.Id, TargetType = PermissionTarget.Role })); + _overwrites = _overwrites.Add(new Overwrite(role.Id, PermissionTarget.Role, new OverwritePermissions(perms.AllowValue, perms.DenyValue))); } public async Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null) { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 1ce105e02..2687312a7 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -55,7 +54,7 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -112,7 +111,7 @@ namespace Discord.Rest #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs index dc4178c3c..664e9c9fc 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs @@ -33,7 +33,7 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS, RequestOptions options = null) @@ -90,7 +90,7 @@ namespace Discord.Rest #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 3e550e452..340fe7f3c 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -72,7 +72,7 @@ namespace Discord.Rest { var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); for (int i = 0; i < model.Emojis.Length; i++) - emojis.Add(GuildEmoji.Create(model.Emojis[i])); + emojis.Add(model.Emojis[i].ToEntity()); _emojis = emojis.ToImmutableArray(); } else diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs index b4b373338..eadda53f2 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuildIntegration.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.Integration; @@ -37,7 +36,7 @@ namespace Discord.Rest return entity; } - public void Update(Model model) + internal void Update(Model model) { Name = model.Name; Type = model.Type; diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs index cd214c2de..12601b72e 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs @@ -28,7 +28,7 @@ namespace Discord.Rest return entity; } - public void Update(Model model) + internal void Update(Model model) { _iconId = model.Icon; IsOwner = model.Owner; diff --git a/src/Discord.Net.Rest/Entities/Messages/Embed.cs b/src/Discord.Net.Rest/Entities/Messages/Embed.cs deleted file mode 100644 index a291dc6c0..000000000 --- a/src/Discord.Net.Rest/Entities/Messages/Embed.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; -using Model = Discord.API.Embed; - -namespace Discord -{ - [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - public class Embed : IEmbed - { - public string Description { get; } - public string Url { get; } - public string Title { get; } - public string Type { get; } - public DateTimeOffset? Timestamp { get; } - public Color? Color { get; } - public EmbedImage? Image { get; } - public EmbedVideo? Video { get; } - public EmbedAuthor? Author { get; } - public EmbedFooter? Footer { get; } - public EmbedProvider? Provider { get; } - public EmbedThumbnail? Thumbnail { get; } - public ImmutableArray Fields { get; } - - internal Embed(string type, - string title, - string description, - string url, - DateTimeOffset? timestamp, - Color? color, - EmbedImage? image, - EmbedVideo? video, - EmbedAuthor? author, - EmbedFooter? footer, - EmbedProvider? provider, - EmbedThumbnail? thumbnail, - ImmutableArray fields) - { - Type = type; - Title = title; - Description = description; - Url = url; - Color = color; - Timestamp = timestamp; - Image = image; - Video = video; - Author = author; - Footer = footer; - Provider = provider; - Thumbnail = thumbnail; - Fields = fields; - } - internal static Embed Create(Model model) - { - return new Embed(model.Type, model.Title, model.Description, model.Url,model.Timestamp, - model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, - model.Image.IsSpecified ? EmbedImage.Create(model.Image.Value) : (EmbedImage?)null, - model.Video.IsSpecified ? EmbedVideo.Create(model.Video.Value) : (EmbedVideo?)null, - model.Author.IsSpecified ? EmbedAuthor.Create(model.Author.Value) : (EmbedAuthor?)null, - model.Footer.IsSpecified ? EmbedFooter.Create(model.Footer.Value) : (EmbedFooter?)null, - model.Provider.IsSpecified ? EmbedProvider.Create(model.Provider.Value) : (EmbedProvider?)null, - model.Thumbnail.IsSpecified ? EmbedThumbnail.Create(model.Thumbnail.Value) : (EmbedThumbnail?)null, - model.Fields.IsSpecified ? model.Fields.Value.Select(EmbedField.Create).ToImmutableArray() : ImmutableArray.Create()); - } - - public override string ToString() => Title; - private string DebuggerDisplay => $"{Title} ({Type})"; - } -} diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs similarity index 58% rename from src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs rename to src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs index 767641f0e..2f5e954be 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs @@ -1,32 +1,28 @@ using System; using System.Collections.Generic; -using Embed = Discord.API.Embed; -using Field = Discord.API.EmbedField; -using Author = Discord.API.EmbedAuthor; -using Footer = Discord.API.EmbedFooter; -using Thumbnail = Discord.API.EmbedThumbnail; -using ImageEmbed = Discord.API.EmbedImage; +using System.Collections.Immutable; namespace Discord { public class EmbedBuilder { - private readonly Embed _model; - private readonly List _fields; + private readonly Embed _embed; + private readonly List _fields; public EmbedBuilder() { - _model = new Embed { Type = "rich" }; - _fields = new List(); + _embed = new Embed("rich"); + _fields = new List(); } - public string Title { get { return _model.Title; } set { _model.Title = value; } } - public string Description { get { return _model.Description; } set { _model.Description = value; } } - public string Url { get { return _model.Url; } set { _model.Url = value; } } - public string ThumbnailUrl { get; set; } - public string ImageUrl { get; set; } - public DateTimeOffset? Timestamp { get; set; } - public Color? Color { get { return _model.Color.HasValue ? new Color(_model.Color.Value) : (Color?)null; } set { _model.Color = value?.RawValue; } } + public string Title { get { return _embed.Title; } set { _embed.Title = value; } } + public string Description { get { return _embed.Description; } set { _embed.Description = value; } } + public string Url { get { return _embed.Url; } set { _embed.Url = value; } } + public string ThumbnailUrl { get { return _embed.Thumbnail?.Url; } set { _embed.Thumbnail = new EmbedThumbnail(value, null, null, null); } } + public string ImageUrl { get { return _embed.Image?.Url; } set { _embed.Image = new EmbedImage(value, null, null, null); } } + public DateTimeOffset? Timestamp { get { return _embed.Timestamp; } set { _embed.Timestamp = value; } } + public Color? Color { get { return _embed.Color; } set { _embed.Color = value; } } + public EmbedAuthorBuilder Author { get; set; } public EmbedFooterBuilder Footer { get; set; } @@ -100,33 +96,34 @@ namespace Discord { var field = new EmbedFieldBuilder(); action(field); - _fields.Add(field.ToModel()); + _fields.Add(field); return this; } - internal Embed Build() + public Embed Build() { - _model.Author = Author?.ToModel(); - _model.Footer = Footer?.ToModel(); - _model.Timestamp = Timestamp?.ToUniversalTime(); - _model.Thumbnail = ThumbnailUrl != null ? new Thumbnail { Url = ThumbnailUrl } : null; - _model.Image = ImageUrl != null ? new ImageEmbed { Url = ImageUrl } : null; - _model.Fields = _fields.ToArray(); - return _model; + _embed.Footer = Footer?.Build(); + _embed.Author = Author?.Build(); + var fields = ImmutableArray.CreateBuilder(_fields.Count); + for (int i = 0; i < _fields.Count; i++) + fields.Add(_fields[i].Build()); + _embed.Fields = fields.ToImmutable(); + return _embed; } + public static implicit operator Embed(EmbedBuilder builder) => builder?.Build(); } public class EmbedFieldBuilder { - private readonly Field _model; + private EmbedField _field; - public string Name { get { return _model.Name; } set { _model.Name = value; } } - public string Value { get { return _model.Value; } set { _model.Value = value; } } - public bool IsInline { get { return _model.Inline; } set { _model.Inline = value; } } + public string Name { get { return _field.Name; } set { _field.Name = value; } } + public string Value { get { return _field.Value; } set { _field.Value = value; } } + public bool IsInline { get { return _field.Inline; } set { _field.Inline = value; } } public EmbedFieldBuilder() { - _model = new Field(); + _field = new EmbedField(); } public EmbedFieldBuilder WithName(string name) @@ -145,20 +142,21 @@ namespace Discord return this; } - internal Field ToModel() => _model; + public EmbedField Build() + => _field; } public class EmbedAuthorBuilder { - private readonly Author _model; + private EmbedAuthor _author; - public string Name { get { return _model.Name; } set { _model.Name = value; } } - public string Url { get { return _model.Url; } set { _model.Url = value; } } - public string IconUrl { get { return _model.IconUrl; } set { _model.IconUrl = value; } } + public string Name { get { return _author.Name; } set { _author.Name = value; } } + public string Url { get { return _author.Url; } set { _author.Url = value; } } + public string IconUrl { get { return _author.IconUrl; } set { _author.IconUrl = value; } } public EmbedAuthorBuilder() { - _model = new Author(); + _author = new EmbedAuthor(); } public EmbedAuthorBuilder WithName(string name) @@ -177,19 +175,20 @@ namespace Discord return this; } - internal Author ToModel() => _model; + public EmbedAuthor Build() + => _author; } public class EmbedFooterBuilder { - private readonly Footer _model; + private EmbedFooter _footer; - public string Text { get { return _model.Text; } set { _model.Text = value; } } - public string IconUrl { get { return _model.IconUrl; } set { _model.IconUrl = value; } } + public string Text { get { return _footer.Text; } set { _footer.Text = value; } } + public string IconUrl { get { return _footer.IconUrl; } set { _footer.IconUrl = value; } } public EmbedFooterBuilder() { - _model = new Footer(); + _footer = new EmbedFooter(); } public EmbedFooterBuilder WithText(string text) @@ -203,6 +202,7 @@ namespace Discord return this; } - internal Footer ToModel() => _model; + public EmbedFooter Build() + => _footer; } } diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index f3aa70676..0afb8d36d 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -18,7 +18,7 @@ namespace Discord.Rest var apiArgs = new API.Rest.ModifyMessageParams { Content = args.Content, - Embed = args.Embed.IsSpecified ? args.Embed.Value.Build() : Optional.Create() + Embed = args.Embed.IsSpecified ? args.Embed.Value.ToModel() : Optional.Create() }; return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs index d957c9145..933833d56 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestReaction.cs @@ -1,6 +1,6 @@ using Model = Discord.API.Reaction; -namespace Discord +namespace Discord.Rest { public class RestReaction : IReaction { @@ -16,7 +16,7 @@ namespace Discord } internal static RestReaction Create(Model model) { - return new RestReaction(Emoji.Create(model.Emoji), model.Count, model.Me); + return new RestReaction(new Emoji(model.Emoji.Id, model.Emoji.Name), model.Count, model.Me); } } } diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 4ae25a142..2d13ed3f7 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -78,7 +78,7 @@ namespace Discord.Rest { var embeds = ImmutableArray.CreateBuilder(value.Length); for (int i = 0; i < value.Length; i++) - embeds.Add(Embed.Create(value[i])); + embeds.Add(value[i].ToEntity()); _embeds = embeds.ToImmutable(); } else diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 5dada416c..9b43b88ac 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -52,7 +52,7 @@ namespace Discord.Rest => UserHelper.CreateDMChannelAsync(this, Discord, options); public override string ToString() => $"{Username}#{Discriminator}"; - internal string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; + private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; //IUser Task IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.Rest/Extensions/EntityExtensions.cs b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs new file mode 100644 index 000000000..7a9643674 --- /dev/null +++ b/src/Discord.Net.Rest/Extensions/EntityExtensions.cs @@ -0,0 +1,124 @@ +using System.Collections.Immutable; +using System.Linq; + +namespace Discord.Rest +{ + internal static class EntityExtensions + { + public static GuildEmoji ToEntity(this API.Emoji model) + { + return new GuildEmoji(model.Id.Value, model.Name, model.Managed, model.RequireColons, ImmutableArray.Create(model.Roles)); + } + + public static Embed ToEntity(this API.Embed model) + { + return new Embed(model.Type, model.Title, model.Description, model.Url, model.Timestamp, + model.Color.HasValue ? new Color(model.Color.Value) : (Color?)null, + model.Image.IsSpecified ? model.Image.Value.ToEntity() : (EmbedImage?)null, + model.Video.IsSpecified ? model.Video.Value.ToEntity() : (EmbedVideo?)null, + model.Author.IsSpecified ? model.Author.Value.ToEntity() : (EmbedAuthor?)null, + model.Footer.IsSpecified ? model.Footer.Value.ToEntity() : (EmbedFooter?)null, + model.Provider.IsSpecified ? model.Provider.Value.ToEntity() : (EmbedProvider?)null, + model.Thumbnail.IsSpecified ? model.Thumbnail.Value.ToEntity() : (EmbedThumbnail?)null, + model.Fields.IsSpecified ? model.Fields.Value.Select(x => x.ToEntity()).ToImmutableArray() : ImmutableArray.Create()); + } + public static API.Embed ToModel(this Embed entity) + { + var model = new API.Embed + { + Type = entity.Type, + Title = entity.Title, + Description = entity.Description, + Url = entity.Url, + Timestamp = entity.Timestamp, + Color = entity.Color?.RawValue + }; + if (entity.Author != null) + model.Author = entity.Author.Value.ToModel(); + model.Fields = entity.Fields.Select(x => x.ToModel()).ToArray(); + if (entity.Footer != null) + model.Footer = entity.Footer.Value.ToModel(); + if (entity.Image != null) + model.Image = entity.Image.Value.ToModel(); + if (entity.Provider != null) + model.Provider = entity.Provider.Value.ToModel(); + if (entity.Thumbnail != null) + model.Thumbnail = entity.Thumbnail.Value.ToModel(); + if (entity.Video != null) + model.Video = entity.Video.Value.ToModel(); + return model; + } + public static EmbedAuthor ToEntity(this API.EmbedAuthor model) + { + return new EmbedAuthor(model.Name, model.Url, model.IconUrl, model.ProxyIconUrl); + } + public static API.EmbedAuthor ToModel(this EmbedAuthor entity) + { + return new API.EmbedAuthor { Name = entity.Name, Url = entity.Url, IconUrl = entity.IconUrl }; + } + public static EmbedField ToEntity(this API.EmbedField model) + { + return new EmbedField(model.Name, model.Value, model.Inline); + } + public static API.EmbedField ToModel(this EmbedField entity) + { + return new API.EmbedField { Name = entity.Name, Value = entity.Value, Inline = entity.Inline }; + } + public static EmbedFooter ToEntity(this API.EmbedFooter model) + { + return new EmbedFooter(model.Text, model.IconUrl, model.ProxyIconUrl); + } + public static API.EmbedFooter ToModel(this EmbedFooter entity) + { + return new API.EmbedFooter { Text = entity.Text, IconUrl = entity.IconUrl }; + } + public static EmbedImage ToEntity(this API.EmbedImage model) + { + return new EmbedImage(model.Url, model.ProxyUrl, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + public static API.EmbedImage ToModel(this EmbedImage entity) + { + return new API.EmbedImage { Url = entity.Url }; + } + public static EmbedProvider ToEntity(this API.EmbedProvider model) + { + return new EmbedProvider(model.Name, model.Url); + } + public static API.EmbedProvider ToModel(this EmbedProvider entity) + { + return new API.EmbedProvider { Name = entity.Name, Url = entity.Url }; + } + public static EmbedThumbnail ToEntity(this API.EmbedThumbnail model) + { + return new EmbedThumbnail(model.Url, model.ProxyUrl, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + public static API.EmbedThumbnail ToModel(this EmbedThumbnail entity) + { + return new API.EmbedThumbnail { Url = entity.Url }; + } + public static EmbedVideo ToEntity(this API.EmbedVideo model) + { + return new EmbedVideo(model.Url, + model.Height.IsSpecified ? model.Height.Value : (int?)null, + model.Width.IsSpecified ? model.Width.Value : (int?)null); + } + public static API.EmbedVideo ToModel(this EmbedVideo entity) + { + return new API.EmbedVideo { Url = entity.Url }; + } + + public static API.Image ToModel(this Image entity) + { + return new API.Image(entity.Stream); + } + + public static Overwrite ToEntity(this API.Overwrite model) + { + return new Overwrite(model.TargetId, model.TargetType, new OverwritePermissions(model.Allow, model.Deny)); + } + } +} diff --git a/src/Discord.Net.Core/Net/Converters/ArrayConverter.cs b/src/Discord.Net.Rest/Net/Converters/ArrayConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/ArrayConverter.cs rename to src/Discord.Net.Rest/Net/Converters/ArrayConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/DiscordContractResolver.cs rename to src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs diff --git a/src/Discord.Net.Core/Net/Converters/ImageConverter.cs b/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/ImageConverter.cs rename to src/Discord.Net.Rest/Net/Converters/ImageConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/NullableConverter.cs b/src/Discord.Net.Rest/Net/Converters/NullableConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/NullableConverter.cs rename to src/Discord.Net.Rest/Net/Converters/NullableConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/OptionalConverter.cs b/src/Discord.Net.Rest/Net/Converters/OptionalConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/OptionalConverter.cs rename to src/Discord.Net.Rest/Net/Converters/OptionalConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/PermissionTargetConverter.cs b/src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/PermissionTargetConverter.cs rename to src/Discord.Net.Rest/Net/Converters/PermissionTargetConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/StringEntityConverter.cs b/src/Discord.Net.Rest/Net/Converters/StringEntityConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/StringEntityConverter.cs rename to src/Discord.Net.Rest/Net/Converters/StringEntityConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/UInt64Converter.cs b/src/Discord.Net.Rest/Net/Converters/UInt64Converter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/UInt64Converter.cs rename to src/Discord.Net.Rest/Net/Converters/UInt64Converter.cs diff --git a/src/Discord.Net.Core/Net/Converters/UInt64EntityConverter.cs b/src/Discord.Net.Rest/Net/Converters/UInt64EntityConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/UInt64EntityConverter.cs rename to src/Discord.Net.Rest/Net/Converters/UInt64EntityConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/UInt64EntityOrIdConverter.cs b/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/UInt64EntityOrIdConverter.cs rename to src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs diff --git a/src/Discord.Net.Core/Net/Converters/UserStatusConverter.cs b/src/Discord.Net.Rest/Net/Converters/UserStatusConverter.cs similarity index 100% rename from src/Discord.Net.Core/Net/Converters/UserStatusConverter.cs rename to src/Discord.Net.Rest/Net/Converters/UserStatusConverter.cs diff --git a/src/Discord.Net.API/Rpc/AuthenticateParams.cs b/src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs similarity index 83% rename from src/Discord.Net.API/Rpc/AuthenticateParams.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs index 9c35c5ede..f4b69a3b7 100644 --- a/src/Discord.Net.API/Rpc/AuthenticateParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/AuthenticateParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class AuthenticateParams + internal class AuthenticateParams { [JsonProperty("access_token")] public string AccessToken { get; set; } diff --git a/src/Discord.Net.API/Rpc/AuthenticateResponse.cs b/src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs similarity index 91% rename from src/Discord.Net.API/Rpc/AuthenticateResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs index 244577f84..6c6cba957 100644 --- a/src/Discord.Net.API/Rpc/AuthenticateResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/AuthenticateResponse.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API.Rpc { - public class AuthenticateResponse + internal class AuthenticateResponse { [JsonProperty("application")] public Application Application { get; set; } diff --git a/src/Discord.Net.API/Rpc/AuthorizeParams.cs b/src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs similarity index 92% rename from src/Discord.Net.API/Rpc/AuthorizeParams.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs index 367aafd41..91678d97c 100644 --- a/src/Discord.Net.API/Rpc/AuthorizeParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/AuthorizeParams.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Discord.API.Rpc { - public class AuthorizeParams + internal class AuthorizeParams { [JsonProperty("client_id")] public string ClientId { get; set; } diff --git a/src/Discord.Net.API/Rpc/AuthorizeResponse.cs b/src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs similarity index 82% rename from src/Discord.Net.API/Rpc/AuthorizeResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs index a4f42b6f5..42a9138fe 100644 --- a/src/Discord.Net.API/Rpc/AuthorizeResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/AuthorizeResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class AuthorizeResponse + internal class AuthorizeResponse { [JsonProperty("code")] public string Code { get; set; } diff --git a/src/Discord.Net.API/Rpc/Channel.cs b/src/Discord.Net.Rpc/API/Rpc/Channel.cs similarity index 97% rename from src/Discord.Net.API/Rpc/Channel.cs rename to src/Discord.Net.Rpc/API/Rpc/Channel.cs index 1b8f3775c..0fc7ac0ee 100644 --- a/src/Discord.Net.API/Rpc/Channel.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Channel.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class Channel + internal class Channel { //Shared [JsonProperty("id")] diff --git a/src/Discord.Net.API/Rpc/ChannelSubscriptionParams.cs b/src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs similarity index 80% rename from src/Discord.Net.API/Rpc/ChannelSubscriptionParams.cs rename to src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs index 70d05e285..7443a6f7d 100644 --- a/src/Discord.Net.API/Rpc/ChannelSubscriptionParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ChannelSubscriptionParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class ChannelSubscriptionParams + internal class ChannelSubscriptionParams { [JsonProperty("channel_id")] public ulong ChannelId { get; set; } diff --git a/src/Discord.Net.API/Rpc/ChannelSummary.cs b/src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs similarity index 89% rename from src/Discord.Net.API/Rpc/ChannelSummary.cs rename to src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs index 34acd049b..43e59c1a1 100644 --- a/src/Discord.Net.API/Rpc/ChannelSummary.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ChannelSummary.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class ChannelSummary + internal class ChannelSummary { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Rpc/ErrorEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs similarity index 89% rename from src/Discord.Net.API/Rpc/ErrorEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs index 2a3cf6506..c59275154 100644 --- a/src/Discord.Net.API/Rpc/ErrorEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ErrorEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class ErrorEvent + internal class ErrorEvent { [JsonProperty("code")] public int Code { get; set; } diff --git a/src/Discord.Net.API/Rpc/ExtendedVoiceState.cs b/src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs similarity index 93% rename from src/Discord.Net.API/Rpc/ExtendedVoiceState.cs rename to src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs index 032914f0f..6722d3a29 100644 --- a/src/Discord.Net.API/Rpc/ExtendedVoiceState.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ExtendedVoiceState.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class ExtendedVoiceState + internal class ExtendedVoiceState { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Rpc/GetChannelParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs similarity index 83% rename from src/Discord.Net.API/Rpc/GetChannelParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs index a94c8740e..4c0e18600 100644 --- a/src/Discord.Net.API/Rpc/GetChannelParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetChannelParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GetChannelParams + internal class GetChannelParams { [JsonProperty("channel_id")] public ulong ChannelId { get; set; } diff --git a/src/Discord.Net.API/Rpc/GetChannelsParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs similarity index 82% rename from src/Discord.Net.API/Rpc/GetChannelsParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs index d2d4409e6..61e4886ef 100644 --- a/src/Discord.Net.API/Rpc/GetChannelsParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetChannelsParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GetChannelsParams + internal class GetChannelsParams { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Rpc/GetChannelsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs similarity index 86% rename from src/Discord.Net.API/Rpc/GetChannelsResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs index e105341a1..004da31b5 100644 --- a/src/Discord.Net.API/Rpc/GetChannelsResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetChannelsResponse.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Discord.API.Rpc { - public class GetChannelsResponse + internal class GetChannelsResponse { [JsonProperty("channels")] public IReadOnlyCollection Channels { get; set; } diff --git a/src/Discord.Net.API/Rpc/GetGuildParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs similarity index 83% rename from src/Discord.Net.API/Rpc/GetGuildParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs index 2fd8e4152..54d5018d0 100644 --- a/src/Discord.Net.API/Rpc/GetGuildParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetGuildParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GetGuildParams + internal class GetGuildParams { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Rpc/GetGuildsParams.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs similarity index 68% rename from src/Discord.Net.API/Rpc/GetGuildsParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs index a1ff5f210..b4350ea21 100644 --- a/src/Discord.Net.API/Rpc/GetGuildsParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetGuildsParams.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class GetGuildsParams + internal class GetGuildsParams { } } diff --git a/src/Discord.Net.API/Rpc/GetGuildsResponse.cs b/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs similarity index 83% rename from src/Discord.Net.API/Rpc/GetGuildsResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs index e69bedeae..4d57ae491 100644 --- a/src/Discord.Net.API/Rpc/GetGuildsResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GetGuildsResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GetGuildsResponse + internal class GetGuildsResponse { [JsonProperty("guilds")] public GuildSummary[] Guilds { get; set; } diff --git a/src/Discord.Net.API/Rpc/Guild.cs b/src/Discord.Net.Rpc/API/Rpc/Guild.cs similarity index 94% rename from src/Discord.Net.API/Rpc/Guild.cs rename to src/Discord.Net.Rpc/API/Rpc/Guild.cs index 1d6bf3678..fde5ef2ee 100644 --- a/src/Discord.Net.API/Rpc/Guild.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Guild.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Discord.API.Rpc { - public class Guild + internal class Guild { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Rpc/GuildMember.cs b/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs similarity index 91% rename from src/Discord.Net.API/Rpc/GuildMember.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildMember.cs index af74dd919..be8fba9dc 100644 --- a/src/Discord.Net.API/Rpc/GuildMember.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GuildMember.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GuildMember + internal class GuildMember { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Rpc/GuildStatusEvent.cs b/src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs similarity index 87% rename from src/Discord.Net.API/Rpc/GuildStatusEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs index 03326c374..3cfbf3454 100644 --- a/src/Discord.Net.API/Rpc/GuildStatusEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GuildStatusEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GuildStatusEvent + internal class GuildStatusEvent { [JsonProperty("guild")] public Guild Guild { get; set; } diff --git a/src/Discord.Net.API/Rpc/GuildSubscriptionParams.cs b/src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs similarity index 80% rename from src/Discord.Net.API/Rpc/GuildSubscriptionParams.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs index 6ad108a5c..a34c71023 100644 --- a/src/Discord.Net.API/Rpc/GuildSubscriptionParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GuildSubscriptionParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class GuildSubscriptionParams + internal class GuildSubscriptionParams { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Rpc/GuildSummary.cs b/src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs similarity index 86% rename from src/Discord.Net.API/Rpc/GuildSummary.cs rename to src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs index c36da5267..09928e16e 100644 --- a/src/Discord.Net.API/Rpc/GuildSummary.cs +++ b/src/Discord.Net.Rpc/API/Rpc/GuildSummary.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class GuildSummary + internal class GuildSummary { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Rpc/Message.cs b/src/Discord.Net.Rpc/API/Rpc/Message.cs similarity index 90% rename from src/Discord.Net.API/Rpc/Message.cs rename to src/Discord.Net.Rpc/API/Rpc/Message.cs index a72fba123..6cbd364bb 100644 --- a/src/Discord.Net.API/Rpc/Message.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Message.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class Message : Discord.API.Message + internal class Message : Discord.API.Message { [JsonProperty("blocked")] public Optional IsBlocked { get; } diff --git a/src/Discord.Net.API/Rpc/MessageEvent.cs b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs similarity index 89% rename from src/Discord.Net.API/Rpc/MessageEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs index 41ff13288..4d656d5e3 100644 --- a/src/Discord.Net.API/Rpc/MessageEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/MessageEvent.cs @@ -2,7 +2,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class MessageEvent + internal class MessageEvent { [JsonProperty("channel_id")] public ulong ChannelId { get; set; } diff --git a/src/Discord.Net.API/Rpc/Pan.cs b/src/Discord.Net.Rpc/API/Rpc/Pan.cs similarity index 90% rename from src/Discord.Net.API/Rpc/Pan.cs rename to src/Discord.Net.Rpc/API/Rpc/Pan.cs index e2a97c369..dc9cbef0a 100644 --- a/src/Discord.Net.API/Rpc/Pan.cs +++ b/src/Discord.Net.Rpc/API/Rpc/Pan.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class Pan + internal class Pan { [JsonProperty("left")] public float Left { get; set; } diff --git a/src/Discord.Net.API/Rpc/ReadyEvent.cs b/src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs similarity index 89% rename from src/Discord.Net.API/Rpc/ReadyEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs index 4cd8c9677..8de69405f 100644 --- a/src/Discord.Net.API/Rpc/ReadyEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/ReadyEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class ReadyEvent + internal class ReadyEvent { [JsonProperty("v")] public int Version { get; set; } diff --git a/src/Discord.Net.API/Rpc/RpcConfig.cs b/src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs similarity index 92% rename from src/Discord.Net.API/Rpc/RpcConfig.cs rename to src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs index c874462b6..4a8928a0d 100644 --- a/src/Discord.Net.API/Rpc/RpcConfig.cs +++ b/src/Discord.Net.Rpc/API/Rpc/RpcConfig.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class RpcConfig + internal class RpcConfig { [JsonProperty("cdn_host")] public string CdnHost { get; set; } diff --git a/src/Discord.Net.API/Rpc/SelectChannelParams.cs b/src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs similarity index 87% rename from src/Discord.Net.API/Rpc/SelectChannelParams.cs rename to src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs index 52c9b00e8..6fc9314a3 100644 --- a/src/Discord.Net.API/Rpc/SelectChannelParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SelectChannelParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SelectChannelParams + internal class SelectChannelParams { [JsonProperty("channel_id")] public ulong? ChannelId { get; set; } diff --git a/src/Discord.Net.API/Rpc/SetLocalVolumeParams.cs b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs similarity index 81% rename from src/Discord.Net.API/Rpc/SetLocalVolumeParams.cs rename to src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs index 9aff39df9..345ad906d 100644 --- a/src/Discord.Net.API/Rpc/SetLocalVolumeParams.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SetLocalVolumeParams + internal class SetLocalVolumeParams { [JsonProperty("volume")] public int Volume { get; set; } diff --git a/src/Discord.Net.API/Rpc/SetLocalVolumeResponse.cs b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs similarity index 85% rename from src/Discord.Net.API/Rpc/SetLocalVolumeResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs index b1dfdc7ef..33927b7d9 100644 --- a/src/Discord.Net.API/Rpc/SetLocalVolumeResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SetLocalVolumeResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SetLocalVolumeResponse + internal class SetLocalVolumeResponse { [JsonProperty("user_id")] public ulong UserId { get; set; } diff --git a/src/Discord.Net.API/Rpc/SpeakingEvent.cs b/src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs similarity index 84% rename from src/Discord.Net.API/Rpc/SpeakingEvent.cs rename to src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs index 4d8804d2f..913d7d768 100644 --- a/src/Discord.Net.API/Rpc/SpeakingEvent.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SpeakingEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SpeakingEvent + internal class SpeakingEvent { [JsonProperty("user_id")] public ulong UserId { get; set; } diff --git a/src/Discord.Net.API/Rpc/SubscriptionResponse.cs b/src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs similarity index 81% rename from src/Discord.Net.API/Rpc/SubscriptionResponse.cs rename to src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs index 7fe13ed6c..76adc8d1b 100644 --- a/src/Discord.Net.API/Rpc/SubscriptionResponse.cs +++ b/src/Discord.Net.Rpc/API/Rpc/SubscriptionResponse.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class SubscriptionResponse + internal class SubscriptionResponse { [JsonProperty("evt")] public string Event { get; set; } diff --git a/src/Discord.Net.API/Rpc/UserVoiceSettings.cs b/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs similarity index 91% rename from src/Discord.Net.API/Rpc/UserVoiceSettings.cs rename to src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs index 9c876a66f..ff338a4a4 100644 --- a/src/Discord.Net.API/Rpc/UserVoiceSettings.cs +++ b/src/Discord.Net.Rpc/API/Rpc/UserVoiceSettings.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class UserVoiceSettings + internal class UserVoiceSettings { [JsonProperty("userId")] internal ulong UserId { get; set; } diff --git a/src/Discord.Net.API/Rpc/VoiceDevice.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceDevice.cs similarity index 87% rename from src/Discord.Net.API/Rpc/VoiceDevice.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceDevice.cs index 4dc99d4cd..52bdef8a3 100644 --- a/src/Discord.Net.API/Rpc/VoiceDevice.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceDevice.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class VoiceDevice + internal class VoiceDevice { [JsonProperty("id")] public string Id { get; set; } diff --git a/src/Discord.Net.API/Rpc/VoiceDeviceSettings.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceDeviceSettings.cs similarity index 90% rename from src/Discord.Net.API/Rpc/VoiceDeviceSettings.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceDeviceSettings.cs index 38473c803..c06eb2c02 100644 --- a/src/Discord.Net.API/Rpc/VoiceDeviceSettings.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceDeviceSettings.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class VoiceDeviceSettings + internal class VoiceDeviceSettings { [JsonProperty("device_id")] public Optional DeviceId { get; set; } diff --git a/src/Discord.Net.API/Rpc/VoiceMode.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceMode.cs similarity index 94% rename from src/Discord.Net.API/Rpc/VoiceMode.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceMode.cs index a502cc960..35e9d453e 100644 --- a/src/Discord.Net.API/Rpc/VoiceMode.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceMode.cs @@ -2,7 +2,7 @@ namespace Discord.API.Rpc { - public class VoiceMode + internal class VoiceMode { [JsonProperty("type")] public Optional Type { get; set; } diff --git a/src/Discord.Net.API/Rpc/VoiceSettings.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceSettings.cs similarity index 96% rename from src/Discord.Net.API/Rpc/VoiceSettings.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceSettings.cs index c3268a719..11fb3b6a8 100644 --- a/src/Discord.Net.API/Rpc/VoiceSettings.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceSettings.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class VoiceSettings + internal class VoiceSettings { [JsonProperty("input")] public VoiceDeviceSettings Input { get; set; } diff --git a/src/Discord.Net.API/Rpc/VoiceShortcut.cs b/src/Discord.Net.Rpc/API/Rpc/VoiceShortcut.cs similarity index 91% rename from src/Discord.Net.API/Rpc/VoiceShortcut.cs rename to src/Discord.Net.Rpc/API/Rpc/VoiceShortcut.cs index 5b0939d79..65e258033 100644 --- a/src/Discord.Net.API/Rpc/VoiceShortcut.cs +++ b/src/Discord.Net.Rpc/API/Rpc/VoiceShortcut.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Rpc { - public class VoiceShortcut + internal class VoiceShortcut { [JsonProperty("type")] public Optional Type { get; set; } diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index e69b5b66d..12b77e5bc 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -21,7 +21,6 @@ - diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs index 0377350fd..05e047067 100644 --- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -17,7 +17,7 @@ using System.Threading.Tasks; namespace Discord.API { - public class DiscordRpcApiClient : DiscordRestApiClient, IDisposable + internal class DiscordRpcApiClient : DiscordRestApiClient, IDisposable { private abstract class RpcRequest { diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 06096d8df..e47cbf30c 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -1,7 +1,6 @@ using Discord.API.Rpc; using Discord.Logging; using Discord.Net.Converters; -using Discord.Net.Queue; using Discord.Rest; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -31,7 +30,7 @@ namespace Discord.Rpc //From DiscordRpcConfig internal int ConnectionTimeout { get; private set; } - public new API.DiscordRpcApiClient ApiClient => base.ApiClient as API.DiscordRpcApiClient; + internal new API.DiscordRpcApiClient ApiClient => base.ApiClient as API.DiscordRpcApiClient; public new RestSelfUser CurrentUser { get { return base.CurrentUser as RestSelfUser; } private set { base.CurrentUser = value; } } public RestApplication ApplicationInfo { get; private set; } @@ -308,20 +307,67 @@ namespace Discord.Rpc var model = await ApiClient.GetVoiceSettingsAsync(options).ConfigureAwait(false); return VoiceSettings.Create(model); } - public async Task SetVoiceSettingsAsync(Action func, RequestOptions options = null) + public async Task SetVoiceSettingsAsync(Action func, RequestOptions options = null) { - var settings = new API.Rpc.VoiceSettings(); - settings.Input = new VoiceDeviceSettings(); - settings.Output = new VoiceDeviceSettings(); - settings.Mode = new VoiceMode(); + if (func == null) throw new NullReferenceException(nameof(func)); + + var settings = new VoiceProperties(); + settings.Input = new VoiceDeviceProperties(); + settings.Output = new VoiceDeviceProperties(); + settings.Mode = new VoiceModeProperties(); func(settings); - await ApiClient.SetVoiceSettingsAsync(settings, options).ConfigureAwait(false); + + var model = new API.Rpc.VoiceSettings + { + AutomaticGainControl = settings.AutomaticGainControl, + EchoCancellation = settings.EchoCancellation, + NoiseSuppression = settings.NoiseSuppression, + QualityOfService = settings.QualityOfService, + SilenceWarning = settings.SilenceWarning + }; + model.Input = new API.Rpc.VoiceDeviceSettings + { + DeviceId = settings.Input.DeviceId, + Volume = settings.Input.Volume + }; + model.Output = new API.Rpc.VoiceDeviceSettings + { + DeviceId = settings.Output.DeviceId, + Volume = settings.Output.Volume + }; + model.Mode = new API.Rpc.VoiceMode + { + AutoThreshold = settings.Mode.AutoThreshold, + Delay = settings.Mode.Delay, + Threshold = settings.Mode.Threshold, + Type = settings.Mode.Type + }; + + if (settings.Input.AvailableDevices.IsSpecified) + model.Input.AvailableDevices = settings.Input.AvailableDevices.Value.Select(x => x.ToModel()).ToArray(); + if (settings.Output.AvailableDevices.IsSpecified) + model.Output.AvailableDevices = settings.Output.AvailableDevices.Value.Select(x => x.ToModel()).ToArray(); + if (settings.Mode.Shortcut.IsSpecified) + model.Mode.Shortcut = settings.Mode.Shortcut.Value.Select(x => x.ToModel()).ToArray(); + + await ApiClient.SetVoiceSettingsAsync(model, options).ConfigureAwait(false); } - public async Task SetUserVoiceSettingsAsync(ulong userId, Action func, RequestOptions options = null) + public async Task SetUserVoiceSettingsAsync(ulong userId, Action func, RequestOptions options = null) { - var settings = new API.Rpc.UserVoiceSettings(); + if (func == null) throw new NullReferenceException(nameof(func)); + + var settings = new UserVoiceProperties(); func(settings); - await ApiClient.SetUserVoiceSettingsAsync(userId, settings, options).ConfigureAwait(false); + + var model = new API.Rpc.UserVoiceSettings + { + Mute = settings.Mute, + UserId = settings.UserId, + Volume = settings.Volume + }; + if (settings.Pan.IsSpecified) + model.Pan = settings.Pan.Value.ToModel(); + await ApiClient.SetUserVoiceSettingsAsync(userId, model, options).ConfigureAwait(false); } private static string GetEventName(RpcGlobalEvent rpcEvent) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs index ba0a31910..1fb6d5867 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcDMChannel.cs @@ -44,7 +44,7 @@ namespace Discord.Rpc public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -108,7 +108,7 @@ namespace Discord.Rpc #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs index 599df7e50..504bf8670 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGroupChannel.cs @@ -46,7 +46,7 @@ namespace Discord.Rpc public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -107,7 +107,7 @@ namespace Discord.Rpc #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs index 0023ccdc1..29df5f643 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -49,7 +49,7 @@ namespace Discord.Rpc public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -109,7 +109,7 @@ namespace Discord.Rpc #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index f7600bbcb..71f35126a 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -84,7 +84,7 @@ namespace Discord.Rpc { var embeds = ImmutableArray.CreateBuilder(value.Length); for (int i = 0; i < value.Length; i++) - embeds.Add(Embed.Create(value[i])); + embeds.Add(value[i].ToEntity()); _embeds = embeds.ToImmutable(); } else diff --git a/src/Discord.Net.Rpc/Entities/UserVoiceProperties.cs b/src/Discord.Net.Rpc/Entities/UserVoiceProperties.cs new file mode 100644 index 000000000..830ba16a3 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/UserVoiceProperties.cs @@ -0,0 +1,18 @@ +#pragma warning disable CS1591 + +using Newtonsoft.Json; + +namespace Discord.Rpc +{ + public class UserVoiceProperties + { + [JsonProperty("userId")] + internal ulong UserId { get; set; } + [JsonProperty("pan")] + public Optional Pan { get; set; } + [JsonProperty("volume")] + public Optional Volume { get; set; } + [JsonProperty("mute")] + public Optional Mute { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index 074286dbc..9f30dc53f 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -47,7 +47,7 @@ namespace Discord.Rpc => UserHelper.CreateDMChannelAsync(this, Discord, options); public override string ToString() => $"{Username}#{Discriminator}"; - internal string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; + private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; //IUser Task IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs index f18a51434..66b4186d6 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcVoiceState.cs @@ -71,7 +71,7 @@ namespace Discord.Rpc } public override string ToString() => User.ToString(); - internal string DebuggerDisplay => $"{User} ({_voiceStates})"; + private string DebuggerDisplay => $"{User} ({_voiceStates})"; string IVoiceState.VoiceSessionId { get { throw new NotSupportedException(); } } IVoiceChannel IVoiceState.VoiceChannel { get { throw new NotSupportedException(); } } diff --git a/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs b/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs new file mode 100644 index 000000000..68409ae95 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Discord.Rpc.Entities.Users +{ + class UserVoiceProperties + { + } +} diff --git a/src/Discord.Net.Rpc/Entities/VoiceDevice.cs b/src/Discord.Net.Rpc/Entities/VoiceDevice.cs index 34a718adc..18f929f93 100644 --- a/src/Discord.Net.Rpc/Entities/VoiceDevice.cs +++ b/src/Discord.Net.Rpc/Entities/VoiceDevice.cs @@ -20,6 +20,6 @@ namespace Discord.Rpc } public override string ToString() => $"{Name}"; - internal string DebuggerDisplay => $"{Name} ({Id})"; + private string DebuggerDisplay => $"{Name} ({Id})"; } } diff --git a/src/Discord.Net.Rpc/Entities/VoiceDeviceProperties.cs b/src/Discord.Net.Rpc/Entities/VoiceDeviceProperties.cs new file mode 100644 index 000000000..bdf87b235 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/VoiceDeviceProperties.cs @@ -0,0 +1,9 @@ +namespace Discord.Rpc +{ + public class VoiceDeviceProperties + { + public Optional DeviceId { get; set; } + public Optional Volume { get; set; } + public Optional AvailableDevices { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/VoiceModeProperties.cs b/src/Discord.Net.Rpc/Entities/VoiceModeProperties.cs new file mode 100644 index 000000000..da791e7a7 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/VoiceModeProperties.cs @@ -0,0 +1,11 @@ +namespace Discord.Rpc +{ + public class VoiceModeProperties + { + public Optional Type { get; set; } + public Optional AutoThreshold { get; set; } + public Optional Threshold { get; set; } + public Optional Shortcut { get; set; } + public Optional Delay { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/VoiceProperties.cs b/src/Discord.Net.Rpc/Entities/VoiceProperties.cs new file mode 100644 index 000000000..5939f83c8 --- /dev/null +++ b/src/Discord.Net.Rpc/Entities/VoiceProperties.cs @@ -0,0 +1,14 @@ +namespace Discord.Rpc +{ + public class VoiceProperties + { + public VoiceDeviceProperties Input { get; set; } + public VoiceDeviceProperties Output { get; set; } + public VoiceModeProperties Mode { get; set; } + public Optional AutomaticGainControl { get; set; } + public Optional EchoCancellation { get; set; } + public Optional NoiseSuppression { get; set; } + public Optional QualityOfService { get; set; } + public Optional SilenceWarning { get; set; } + } +} diff --git a/src/Discord.Net.Rpc/Entities/VoiceShortcut.cs b/src/Discord.Net.Rpc/Entities/VoiceShortcut.cs index 93ef21804..ea9be9977 100644 --- a/src/Discord.Net.Rpc/Entities/VoiceShortcut.cs +++ b/src/Discord.Net.Rpc/Entities/VoiceShortcut.cs @@ -22,6 +22,6 @@ namespace Discord.Rpc } public override string ToString() => $"{Name}"; - internal string DebuggerDisplay => $"{Name} ({Code}, {Type})"; + private string DebuggerDisplay => $"{Name} ({Code}, {Type})"; } } diff --git a/src/Discord.Net.API/Rpc/VoiceShortcutType.cs b/src/Discord.Net.Rpc/Entities/VoiceShortcutType.cs similarity index 100% rename from src/Discord.Net.API/Rpc/VoiceShortcutType.cs rename to src/Discord.Net.Rpc/Entities/VoiceShortcutType.cs diff --git a/src/Discord.Net.Rpc/Extensions/EntityExtensions.cs b/src/Discord.Net.Rpc/Extensions/EntityExtensions.cs new file mode 100644 index 000000000..00ccf5c68 --- /dev/null +++ b/src/Discord.Net.Rpc/Extensions/EntityExtensions.cs @@ -0,0 +1,31 @@ +namespace Discord.Rpc +{ + internal static class EntityExtensions + { + public static API.Rpc.Pan ToModel(this Pan entity) + { + return new API.Rpc.Pan + { + Left = entity.Left, + Right = entity.Right + }; + } + public static API.Rpc.VoiceDevice ToModel(this VoiceDevice entity) + { + return new API.Rpc.VoiceDevice + { + Id = entity.Id, + Name = entity.Name + }; + } + public static API.Rpc.VoiceShortcut ToModel(this VoiceShortcut entity) + { + return new API.Rpc.VoiceShortcut + { + Code = entity.Code, + Name = entity.Name, + Type = entity.Type + }; + } + } +} diff --git a/src/Discord.Net.API/Gateway/ExtendedGuild.cs b/src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs similarity index 94% rename from src/Discord.Net.API/Gateway/ExtendedGuild.cs rename to src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs index 5d5fb8001..910f6d909 100644 --- a/src/Discord.Net.API/Gateway/ExtendedGuild.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/ExtendedGuild.cs @@ -4,7 +4,7 @@ using System; namespace Discord.API.Gateway { - public class ExtendedGuild : Guild + internal class ExtendedGuild : Guild { [JsonProperty("unavailable")] public bool? Unavailable { get; set; } diff --git a/src/Discord.Net.API/Gateway/GatewayOpCode.cs b/src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs similarity index 97% rename from src/Discord.Net.API/Gateway/GatewayOpCode.cs rename to src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs index b6712922a..13a2bb462 100644 --- a/src/Discord.Net.API/Gateway/GatewayOpCode.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GatewayOpCode.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API.Gateway { - public enum GatewayOpCode : byte + internal enum GatewayOpCode : byte { /// C←S - Used to send most events. Dispatch = 0, diff --git a/src/Discord.Net.API/Gateway/GuildBanEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs similarity index 88% rename from src/Discord.Net.API/Gateway/GuildBanEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs index c3c4a02ce..59a3304dd 100644 --- a/src/Discord.Net.API/Gateway/GuildBanEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildBanEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildBanEvent + internal class GuildBanEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildEmojiUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/GuildEmojiUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs index 5d895cbf8..715341dc5 100644 --- a/src/Discord.Net.API/Gateway/GuildEmojiUpdateEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildEmojiUpdateEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildEmojiUpdateEvent + internal class GuildEmojiUpdateEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildMemberAddEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs similarity index 77% rename from src/Discord.Net.API/Gateway/GuildMemberAddEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs index 856fb01f9..350652faf 100644 --- a/src/Discord.Net.API/Gateway/GuildMemberAddEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberAddEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildMemberAddEvent : GuildMember + internal class GuildMemberAddEvent : GuildMember { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildMemberRemoveEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs similarity index 85% rename from src/Discord.Net.API/Gateway/GuildMemberRemoveEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs index 4a9b0fc07..501408a7f 100644 --- a/src/Discord.Net.API/Gateway/GuildMemberRemoveEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberRemoveEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildMemberRemoveEvent + internal class GuildMemberRemoveEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildMemberUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs similarity index 76% rename from src/Discord.Net.API/Gateway/GuildMemberUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs index 74a67bce2..a234d6da5 100644 --- a/src/Discord.Net.API/Gateway/GuildMemberUpdateEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildMemberUpdateEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildMemberUpdateEvent : GuildMember + internal class GuildMemberUpdateEvent : GuildMember { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildMembersChunkEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/GuildMembersChunkEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs index b5f2e82f2..e401d7fa1 100644 --- a/src/Discord.Net.API/Gateway/GuildMembersChunkEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildMembersChunkEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildMembersChunkEvent + internal class GuildMembersChunkEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildRoleCreateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/GuildRoleCreateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs index 50f067eb1..3409b1c91 100644 --- a/src/Discord.Net.API/Gateway/GuildRoleCreateEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleCreateEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildRoleCreateEvent + internal class GuildRoleCreateEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildRoleDeleteEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/GuildRoleDeleteEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs index 05203df7f..dbdaeff67 100644 --- a/src/Discord.Net.API/Gateway/GuildRoleDeleteEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleDeleteEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildRoleDeleteEvent + internal class GuildRoleDeleteEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildRoleUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/GuildRoleUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs index fde9c5907..b04ecb182 100644 --- a/src/Discord.Net.API/Gateway/GuildRoleUpdateEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildRoleUpdateEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildRoleUpdateEvent + internal class GuildRoleUpdateEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/GuildSyncEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs similarity index 92% rename from src/Discord.Net.API/Gateway/GuildSyncEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs index 9a34fb8b8..6b2e6c02f 100644 --- a/src/Discord.Net.API/Gateway/GuildSyncEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/GuildSyncEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class GuildSyncEvent + internal class GuildSyncEvent { [JsonProperty("id")] public ulong Id { get; set; } diff --git a/src/Discord.Net.API/Gateway/HelloEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs similarity index 86% rename from src/Discord.Net.API/Gateway/HelloEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs index 1f03bc037..e1ed9463c 100644 --- a/src/Discord.Net.API/Gateway/HelloEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/HelloEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class HelloEvent + internal class HelloEvent { [JsonProperty("heartbeat_interval")] public int HeartbeatInterval { get; set; } diff --git a/src/Discord.Net.API/Gateway/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs similarity index 95% rename from src/Discord.Net.API/Gateway/IdentifyParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs index 22aee7daa..e87c58221 100644 --- a/src/Discord.Net.API/Gateway/IdentifyParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Discord.API.Gateway { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class IdentifyParams + internal class IdentifyParams { [JsonProperty("token")] public string Token { get; set; } diff --git a/src/Discord.Net.API/Gateway/MessageDeleteBulkEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs similarity index 87% rename from src/Discord.Net.API/Gateway/MessageDeleteBulkEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs index 7a3df47b2..aba4a2f1c 100644 --- a/src/Discord.Net.API/Gateway/MessageDeleteBulkEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace Discord.API.Gateway { - public class MessageDeleteBulkEvent + internal class MessageDeleteBulkEvent { [JsonProperty("channel_id")] public ulong ChannelId { get; set; } diff --git a/src/Discord.Net.API/Gateway/Reaction.cs b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs similarity index 93% rename from src/Discord.Net.API/Gateway/Reaction.cs rename to src/Discord.Net.WebSocket/API/Gateway/Reaction.cs index 2ed22a97b..62de456e2 100644 --- a/src/Discord.Net.API/Gateway/Reaction.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/Reaction.cs @@ -2,7 +2,7 @@ namespace Discord.API.Gateway { - public class Reaction + internal class Reaction { [JsonProperty("user_id")] public ulong UserId { get; set; } diff --git a/src/Discord.Net.API/Gateway/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs similarity index 97% rename from src/Discord.Net.API/Gateway/ReadyEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs index a677ac281..ab92d8c36 100644 --- a/src/Discord.Net.API/Gateway/ReadyEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/ReadyEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class ReadyEvent + internal class ReadyEvent { public class ReadState { diff --git a/src/Discord.Net.API/Gateway/RecipientEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs similarity index 88% rename from src/Discord.Net.API/Gateway/RecipientEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs index 308b4958f..336ffd029 100644 --- a/src/Discord.Net.API/Gateway/RecipientEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/RecipientEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class RecipientEvent + internal class RecipientEvent { [JsonProperty("user")] public User User { get; set; } diff --git a/src/Discord.Net.API/Gateway/RemoveAllReactionsEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs similarity index 84% rename from src/Discord.Net.API/Gateway/RemoveAllReactionsEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs index 944a6e7c9..4833c5123 100644 --- a/src/Discord.Net.API/Gateway/RemoveAllReactionsEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/RemoveAllReactionsEvent.cs @@ -2,7 +2,7 @@ namespace Discord.API.Gateway { - public class RemoveAllReactionsEvent + internal class RemoveAllReactionsEvent { [JsonProperty("channel_id")] public ulong ChannelId { get; set; } diff --git a/src/Discord.Net.API/Gateway/RequestMembersParams.cs b/src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs similarity index 91% rename from src/Discord.Net.API/Gateway/RequestMembersParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs index 05ec87f56..6a8d283ed 100644 --- a/src/Discord.Net.API/Gateway/RequestMembersParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; namespace Discord.API.Gateway { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class RequestMembersParams + internal class RequestMembersParams { [JsonProperty("query")] public string Query { get; set; } diff --git a/src/Discord.Net.API/Gateway/ResumeParams.cs b/src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs similarity index 92% rename from src/Discord.Net.API/Gateway/ResumeParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs index 219adcf16..ffb46327b 100644 --- a/src/Discord.Net.API/Gateway/ResumeParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/ResumeParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class ResumeParams + internal class ResumeParams { [JsonProperty("token")] public string Token { get; set; } diff --git a/src/Discord.Net.API/Gateway/ResumedEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs similarity index 85% rename from src/Discord.Net.API/Gateway/ResumedEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs index 398a716fd..d1347beae 100644 --- a/src/Discord.Net.API/Gateway/ResumedEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/ResumedEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class ResumedEvent + internal class ResumedEvent { [JsonProperty("heartbeat_interval")] public int HeartbeatInterval { get; set; } diff --git a/src/Discord.Net.API/Gateway/StatusUpdateParams.cs b/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs similarity index 92% rename from src/Discord.Net.API/Gateway/StatusUpdateParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs index ae1f79283..fc0964c17 100644 --- a/src/Discord.Net.API/Gateway/StatusUpdateParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/StatusUpdateParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class StatusUpdateParams + internal class StatusUpdateParams { [JsonProperty("status")] public UserStatus Status { get; set; } diff --git a/src/Discord.Net.API/Gateway/TypingStartEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs similarity index 90% rename from src/Discord.Net.API/Gateway/TypingStartEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs index 3a6cd3d5c..3cce512bd 100644 --- a/src/Discord.Net.API/Gateway/TypingStartEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class TypingStartEvent + internal class TypingStartEvent { [JsonProperty("user_id")] public ulong UserId { get; set; } diff --git a/src/Discord.Net.API/Gateway/VoiceServerUpdateEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs similarity index 88% rename from src/Discord.Net.API/Gateway/VoiceServerUpdateEvent.cs rename to src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs index a300f0d2c..29167c1cc 100644 --- a/src/Discord.Net.API/Gateway/VoiceServerUpdateEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/VoiceServerUpdateEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { - public class VoiceServerUpdateEvent + internal class VoiceServerUpdateEvent { [JsonProperty("guild_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs b/src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs similarity index 92% rename from src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs rename to src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs index f08973cb5..521160126 100644 --- a/src/Discord.Net.API/Gateway/VoiceStateUpdateParams.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/VoiceStateUpdateParams.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Discord.API.Gateway { [JsonObject(MemberSerialization = MemberSerialization.OptIn)] - public class VoiceStateUpdateParams + internal class VoiceStateUpdateParams { [JsonProperty("self_mute")] public bool SelfMute { get; set; } diff --git a/src/Discord.Net.API/Voice/IdentifyParams.cs b/src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs similarity index 92% rename from src/Discord.Net.API/Voice/IdentifyParams.cs rename to src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs index e6a1fd288..d446867e1 100644 --- a/src/Discord.Net.API/Voice/IdentifyParams.cs +++ b/src/Discord.Net.WebSocket/API/Voice/IdentifyParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class IdentifyParams + internal class IdentifyParams { [JsonProperty("server_id")] public ulong GuildId { get; set; } diff --git a/src/Discord.Net.API/Voice/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs similarity index 93% rename from src/Discord.Net.API/Voice/ReadyEvent.cs rename to src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs index 0fe52a587..e4446f814 100644 --- a/src/Discord.Net.API/Voice/ReadyEvent.cs +++ b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class ReadyEvent + internal class ReadyEvent { [JsonProperty("ssrc")] public uint SSRC { get; set; } diff --git a/src/Discord.Net.API/Voice/SelectProtocolParams.cs b/src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs similarity index 86% rename from src/Discord.Net.API/Voice/SelectProtocolParams.cs rename to src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs index 4c4e3ca32..8c577e5b5 100644 --- a/src/Discord.Net.API/Voice/SelectProtocolParams.cs +++ b/src/Discord.Net.WebSocket/API/Voice/SelectProtocolParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class SelectProtocolParams + internal class SelectProtocolParams { [JsonProperty("protocol")] public string Protocol { get; set; } diff --git a/src/Discord.Net.API/Voice/SessionDescriptionEvent.cs b/src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs similarity index 85% rename from src/Discord.Net.API/Voice/SessionDescriptionEvent.cs rename to src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs index 5a8ec8a41..45befadcf 100644 --- a/src/Discord.Net.API/Voice/SessionDescriptionEvent.cs +++ b/src/Discord.Net.WebSocket/API/Voice/SessionDescriptionEvent.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class SessionDescriptionEvent + internal class SessionDescriptionEvent { [JsonProperty("secret_key")] public byte[] SecretKey { get; set; } diff --git a/src/Discord.Net.API/Voice/SpeakingParams.cs b/src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs similarity index 88% rename from src/Discord.Net.API/Voice/SpeakingParams.cs rename to src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs index ee0e737dc..abdf90667 100644 --- a/src/Discord.Net.API/Voice/SpeakingParams.cs +++ b/src/Discord.Net.WebSocket/API/Voice/SpeakingParams.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class SpeakingParams + internal class SpeakingParams { [JsonProperty("speaking")] public bool IsSpeaking { get; set; } diff --git a/src/Discord.Net.API/Voice/UdpProtocolInfo.cs b/src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs similarity index 90% rename from src/Discord.Net.API/Voice/UdpProtocolInfo.cs rename to src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs index eb57471a4..6f4719e7e 100644 --- a/src/Discord.Net.API/Voice/UdpProtocolInfo.cs +++ b/src/Discord.Net.WebSocket/API/Voice/UdpProtocolInfo.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Discord.API.Voice { - public class UdpProtocolInfo + internal class UdpProtocolInfo { [JsonProperty("address")] public string Address { get; set; } diff --git a/src/Discord.Net.API/Voice/VoiceOpCode.cs b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs similarity index 96% rename from src/Discord.Net.API/Voice/VoiceOpCode.cs rename to src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs index e7cda249d..ae11a4c8f 100644 --- a/src/Discord.Net.API/Voice/VoiceOpCode.cs +++ b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 namespace Discord.API.Voice { - public enum VoiceOpCode : byte + internal enum VoiceOpCode : byte { /// C→S - Used to associate a connection with a token. Identify = 0, diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 593d9e75e..0887e3554 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -17,7 +17,6 @@ - diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 403f1e239..4bda2b479 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -22,7 +22,7 @@ namespace Discord.WebSocket internal UserStatus Status => _shards[0].Status; internal Game? Game => _shards[0].Game; - public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; + internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } public IReadOnlyCollection Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount()); public IReadOnlyCollection PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(() => GetPrivateChannelCount()); @@ -150,11 +150,8 @@ namespace Discord.WebSocket public DiscordSocketClient GetShard(int id) { - for (int i = 0; i < _shards.Length; i++) - { - if (_shards[i].ShardId == id) - return _shards[i]; - } + if (_shardIdsToIndex.TryGetValue(id, out id)) + return _shards[id]; return null; } private int GetShardIdFor(ulong guildId) @@ -162,12 +159,7 @@ namespace Discord.WebSocket private int GetShardIdFor(IGuild guild) => GetShardIdFor(guild.Id); private DiscordSocketClient GetShardFor(ulong guildId) - { - int id = GetShardIdFor(guildId); - if (_shardIdsToIndex.TryGetValue(id, out id)) - return _shards[id]; - return null; - } + => GetShard(GetShardIdFor(guildId)); private DiscordSocketClient GetShardFor(IGuild guild) => GetShardFor(guild.Id); diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index bcc8e40b7..d5fbcc71d 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; namespace Discord.API { - public class DiscordSocketApiClient : DiscordRestApiClient + internal class DiscordSocketApiClient : DiscordRestApiClient { public event Func SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } } private readonly AsyncEvent> _sentGatewayMessageEvent = new AsyncEvent>(); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index a93ba5fcf..58c27dccc 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -60,7 +60,7 @@ namespace Discord.WebSocket internal WebSocketProvider WebSocketProvider { get; private set; } internal bool DownloadUsersOnGuildAvailable { get; private set; } - public new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; + internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } public IReadOnlyCollection Guilds => State.Guilds; public IReadOnlyCollection PrivateChannels => State.PrivateChannels; diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index 1773214ad..c8a857074 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; namespace Discord.Audio { - public class DiscordVoiceAPIClient + internal class DiscordVoiceAPIClient { public const int MaxBitrate = 128 * 1024; public const string Mode = "xsalsa20_poly1305"; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index 0f8e1e156..43246f5ca 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -11,7 +11,7 @@ namespace Discord.WebSocket IReadOnlyCollection CachedMessages { get; } /// Sends a message to this message channel. - new Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null); + new Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if NETSTANDARD1_3 /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, RequestOptions options = null); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 34f67c1f0..c976b64f8 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -66,7 +66,7 @@ namespace Discord.WebSocket public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -138,7 +138,7 @@ namespace Discord.WebSocket #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 706d972df..ceba50a6e 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -95,7 +95,7 @@ namespace Discord.WebSocket public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -207,7 +207,7 @@ namespace Discord.WebSocket #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index be99f3f9f..58c93ebaf 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -47,7 +47,7 @@ namespace Discord.WebSocket var overwrites = model.PermissionOverwrites.Value; var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); for (int i = 0; i < overwrites.Length; i++) - newOverwrites.Add(new Overwrite(overwrites[i])); + newOverwrites.Add(overwrites[i].ToEntity()); _overwrites = newOverwrites.ToImmutable(); } @@ -77,12 +77,12 @@ namespace Discord.WebSocket public async Task AddPermissionOverwriteAsync(IUser user, OverwritePermissions perms, RequestOptions options = null) { await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, user, perms, options).ConfigureAwait(false); - _overwrites = _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = user.Id, TargetType = PermissionTarget.User })); + _overwrites = _overwrites.Add(new Overwrite(user.Id, PermissionTarget.User, new OverwritePermissions(perms.AllowValue, perms.DenyValue))); } public async Task AddPermissionOverwriteAsync(IRole role, OverwritePermissions perms, RequestOptions options = null) { await ChannelHelper.AddPermissionOverwriteAsync(this, Discord, role, perms, options).ConfigureAwait(false); - _overwrites.Add(new Overwrite(new API.Overwrite { Allow = perms.AllowValue, Deny = perms.DenyValue, TargetId = role.Id, TargetType = PermissionTarget.Role })); + _overwrites = _overwrites.Add(new Overwrite(role.Id, PermissionTarget.Role, new OverwritePermissions(perms.AllowValue, perms.DenyValue))); } public async Task RemovePermissionOverwriteAsync(IUser user, RequestOptions options = null) { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 4b1ec4ae8..ae2564959 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -72,7 +72,7 @@ namespace Discord.WebSocket public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, EmbedBuilder embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if NETSTANDARD1_3 public Task SendFileAsync(string filePath, string text, bool isTTS = false, RequestOptions options = null) @@ -139,7 +139,7 @@ namespace Discord.WebSocket #endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, options).ConfigureAwait(false); - async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, EmbedBuilder embed, RequestOptions options) + async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index eddee15c7..b46107dc6 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -191,7 +191,7 @@ namespace Discord.WebSocket { var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); for (int i = 0; i < model.Emojis.Length; i++) - emojis.Add(GuildEmoji.Create(model.Emojis[i])); + emojis.Add(model.Emojis[i].ToEntity()); _emojis = emojis.ToImmutable(); } else @@ -244,7 +244,7 @@ namespace Discord.WebSocket { var emojis = ImmutableArray.CreateBuilder(model.Emojis.Length); for (int i = 0; i < model.Emojis.Length; i++) - emojis.Add(GuildEmoji.Create(model.Emojis[i])); + emojis.Add(model.Emojis[i].ToEntity()); _emojis = emojis.ToImmutable(); } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs index 3e56644b1..c12d0fdea 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketReaction.cs @@ -22,7 +22,7 @@ namespace Discord.WebSocket } internal static SocketReaction Create(Model model, ISocketMessageChannel channel, Optional message, Optional user) { - return new SocketReaction(channel, model.MessageId, message, model.UserId, user, Emoji.Create(model.Emoji)); + return new SocketReaction(channel, model.MessageId, message, model.UserId, user, new Emoji(model.Emoji.Id, model.Emoji.Name)); } } } diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index e50076986..e1a6853e2 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -79,7 +79,7 @@ namespace Discord.WebSocket { var embeds = ImmutableArray.CreateBuilder(value.Length); for (int i = 0; i < value.Length; i++) - embeds.Add(Embed.Create(value[i])); + embeds.Add(value[i].ToEntity()); _embeds = embeds.ToImmutable(); } else diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs index 629aa2093..00d4b4bbc 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketPresence.cs @@ -17,11 +17,11 @@ namespace Discord.WebSocket } internal static SocketPresence Create(Model model) { - return new SocketPresence(model.Status, model.Game != null ? Discord.Game.Create(model.Game) : (Game?)null); + return new SocketPresence(model.Status, model.Game != null ? model.Game.ToEntity() : (Game?)null); } public override string ToString() => Status.ToString(); - internal string DebuggerDisplay => $"{Status}{(Game != null ? $", {Game.Value.Name} ({Game.Value.StreamType})" : "")}"; + private string DebuggerDisplay => $"{Status}{(Game != null ? $", {Game.Value.Name} ({Game.Value.StreamType})" : "")}"; internal SocketPresence Clone() => this; } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 4819de751..b94da2438 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -46,7 +46,7 @@ namespace Discord.WebSocket => UserHelper.CreateDMChannelAsync(this, Discord, options); public override string ToString() => $"{Username}#{Discriminator}"; - internal string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; + private string DebuggerDisplay => $"{Username}#{Discriminator} ({Id}{(IsBot ? ", Bot" : "")})"; internal SocketUser Clone() => MemberwiseClone() as SocketUser; //IUser diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs index 2a0eeed8f..480103326 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketVoiceState.cs @@ -56,7 +56,7 @@ namespace Discord.WebSocket } public override string ToString() => VoiceChannel?.Name ?? "Unknown"; - internal string DebuggerDisplay => $"{VoiceChannel?.Name ?? "Unknown"} ({_voiceStates})"; + private string DebuggerDisplay => $"{VoiceChannel?.Name ?? "Unknown"} ({_voiceStates})"; internal SocketVoiceState Clone() => this; IVoiceChannel IVoiceState.VoiceChannel => VoiceChannel; diff --git a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs new file mode 100644 index 000000000..636ef68f4 --- /dev/null +++ b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs @@ -0,0 +1,12 @@ +namespace Discord.WebSocket +{ + internal static class EntityExtensions + { + public static Game ToEntity(this API.Game model) + { + return new Game(model.Name, + model.StreamUrl.GetValueOrDefault(null), + model.StreamType.GetValueOrDefault(null) ?? StreamType.NotStreaming); + } + } +} diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 4d9388915..4645b3c7e 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,4 +1,4 @@ - + An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0-beta2 @@ -16,7 +16,6 @@ - From 737fd3dc83e510ba3f87828d39ed8de88d267972 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 23:32:43 -0400 Subject: [PATCH 136/263] Removed leftover Discord.Net.API files --- src/Discord.Net.API/Discord.Net.API.csproj | 29 ----------------- src/Discord.Net.API/project.json | 38 ---------------------- 2 files changed, 67 deletions(-) delete mode 100644 src/Discord.Net.API/Discord.Net.API.csproj delete mode 100644 src/Discord.Net.API/project.json diff --git a/src/Discord.Net.API/Discord.Net.API.csproj b/src/Discord.Net.API/Discord.Net.API.csproj deleted file mode 100644 index aa4462e9e..000000000 --- a/src/Discord.Net.API/Discord.Net.API.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - A collection of Discord API definitions for use by Discord.Net. - 1.0.0-beta2 - netstandard1.1 - Discord.Net.API - discord;discordapp - https://github.com/RogueException/Discord.Net - http://opensource.org/licenses/MIT - git - git://github.com/RogueException/Discord.Net.API - Discord - - - - - - - - - - - - - $(NoWarn);CS1573;CS1591 - true - true - - \ No newline at end of file diff --git a/src/Discord.Net.API/project.json b/src/Discord.Net.API/project.json deleted file mode 100644 index 41f09804d..000000000 --- a/src/Discord.Net.API/project.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A collection of Discord API definitions for use by Discord.Net", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Newtonsoft.Json": "9.0.1", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.1.1" - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": {} - } -} From 4a75f68ac8a4e8870fce19e3654b9e06439c4506 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 23:40:19 -0400 Subject: [PATCH 137/263] Added ManageWebhooks to Channel/OverwritePermissions --- .../Entities/Permissions/ChannelPermission.cs | 2 +- .../Entities/Permissions/ChannelPermissions.cs | 13 ++++++++----- .../Entities/Permissions/GuildPermissions.cs | 2 +- .../Entities/Permissions/OverwritePermissions.cs | 14 +++++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs index 7698390f1..a93f02497 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs @@ -34,7 +34,7 @@ //ChangeNickname = 26, //ManageNicknames = 27, ManagePermissions = 28, - //ManageWebhooks = 29, + ManageWebhooks = 29, //ManageEmojis = 30 } } diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 93c030f4a..2824a1426 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -71,6 +71,8 @@ namespace Discord /// If True, a user may adjust permissions. This also implictly grants all other permissions. public bool ManagePermissions => Permissions.GetValue(RawValue, ChannelPermission.ManagePermissions); + /// If True, a user may edit the webhooks for this channel. + public bool ManageWebhooks => Permissions.GetValue(RawValue, ChannelPermission.ManageWebhooks); /// Creates a new ChannelPermissions with the provided packed value. public ChannelPermissions(ulong rawValue) { RawValue = rawValue; } @@ -80,7 +82,7 @@ namespace Discord bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool? useExternalEmojis = null, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) + bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null, bool? manageWebhooks = null) { ulong value = initialValue; @@ -103,6 +105,7 @@ namespace Discord Permissions.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers); Permissions.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD); Permissions.SetValue(ref value, managePermissions, ChannelPermission.ManagePermissions); + Permissions.SetValue(ref value, manageWebhooks, ChannelPermission.ManageWebhooks); RawValue = value; } @@ -113,10 +116,10 @@ namespace Discord bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, bool embedLinks = false, bool attachFiles = false, bool readMessageHistory = false, bool mentionEveryone = false, bool useExternalEmojis = false, bool connect = false, bool speak = false, bool muteMembers = false, bool deafenMembers = false, - bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false) + bool moveMembers = false, bool useVoiceActivation = false, bool managePermissions = false, bool manageWebhooks = false) : this(0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions) { } + speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks) { } /// Creates a new ChannelPermissions from this one, changing the provided non-null permissions. public ChannelPermissions Modify(bool? createInstantInvite = null, bool? manageChannel = null, @@ -124,10 +127,10 @@ namespace Discord bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, bool? embedLinks = null, bool? attachFiles = null, bool? readMessageHistory = null, bool? mentionEveryone = null, bool useExternalEmojis = false, bool? connect = null, bool? speak = null, bool? muteMembers = null, bool? deafenMembers = null, - bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null) + bool? moveMembers = null, bool? useVoiceActivation = null, bool? managePermissions = null, bool? manageWebhooks = null) => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions); + speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, managePermissions, manageWebhooks); public bool Has(ChannelPermission permission) => Permissions.GetValue(RawValue, permission); diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 1badf7d30..e7461915c 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -69,7 +69,7 @@ namespace Discord public bool ManageNicknames => Permissions.GetValue(RawValue, GuildPermission.ManageNicknames); /// If True, a user may adjust roles. public bool ManageRoles => Permissions.GetValue(RawValue, GuildPermission.ManageRoles); - /// If True, a user may edit the emojis for this guild. + /// If True, a user may edit the webhooks for this guild. public bool ManageWebhooks => Permissions.GetValue(RawValue, GuildPermission.ManageWebhooks); /// If True, a user may edit the emojis for this guild. public bool ManageEmojis => Permissions.GetValue(RawValue, GuildPermission.ManageEmojis); diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs index f21155475..c3f8b2bab 100644 --- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs @@ -60,6 +60,8 @@ namespace Discord /// If Allowed, a user may adjust permissions. This also implictly grants all other permissions. public PermValue ManagePermissions => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManagePermissions); + /// If True, a user may edit the webhooks for this channel. + public PermValue ManageWebhooks => Permissions.GetValue(AllowValue, DenyValue, ChannelPermission.ManageWebhooks); /// Creates a new OverwritePermissions with the provided allow and deny packed values. public OverwritePermissions(ulong allowValue, ulong denyValue) @@ -73,7 +75,8 @@ namespace Discord PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, - PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null) + PermValue? deafenMembers = null, PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null, + PermValue? manageWebhooks = null) { Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); Permissions.SetValue(ref allowValue, ref denyValue, manageChannel, ChannelPermission.ManageChannel); @@ -94,6 +97,7 @@ namespace Discord Permissions.SetValue(ref allowValue, ref denyValue, moveMembers, ChannelPermission.MoveMembers); Permissions.SetValue(ref allowValue, ref denyValue, useVoiceActivation, ChannelPermission.UseVAD); Permissions.SetValue(ref allowValue, ref denyValue, managePermissions, ChannelPermission.ManagePermissions); + Permissions.SetValue(ref allowValue, ref denyValue, manageWebhooks, ChannelPermission.ManageWebhooks); AllowValue = allowValue; DenyValue = denyValue; @@ -105,10 +109,10 @@ namespace Discord PermValue readMessages = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit, PermValue embedLinks = PermValue.Inherit, PermValue attachFiles = PermValue.Inherit, PermValue readMessageHistory = PermValue.Inherit, PermValue mentionEveryone = PermValue.Inherit, PermValue useExternalEmojis = PermValue.Inherit, PermValue connect = PermValue.Inherit, PermValue speak = PermValue.Inherit, PermValue muteMembers = PermValue.Inherit, PermValue deafenMembers = PermValue.Inherit, - PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue managePermissions = PermValue.Inherit) + PermValue moveMembers = PermValue.Inherit, PermValue useVoiceActivation = PermValue.Inherit, PermValue managePermissions = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit) : this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, - moveMembers, useVoiceActivation, managePermissions) { } + moveMembers, useVoiceActivation, managePermissions, manageWebhooks) { } /// Creates a new OverwritePermissions from this one, changing the provided non-null permissions. public OverwritePermissions Modify(PermValue? createInstantInvite = null, PermValue? manageChannel = null, @@ -116,10 +120,10 @@ namespace Discord PermValue? readMessages = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, PermValue? embedLinks = null, PermValue? attachFiles = null, PermValue? readMessageHistory = null, PermValue? mentionEveryone = null, PermValue? useExternalEmojis = null, PermValue? connect = null, PermValue? speak = null, PermValue? muteMembers = null, PermValue? deafenMembers = null, - PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null) + PermValue? moveMembers = null, PermValue? useVoiceActivation = null, PermValue? managePermissions = null, PermValue? manageWebhooks = null) => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, - moveMembers, useVoiceActivation, managePermissions); + moveMembers, useVoiceActivation, managePermissions, manageWebhooks); public List ToAllowList() { From 02b4e0c3c11e4ab23e74e67e73230bd6352eccbc Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 23:55:18 -0400 Subject: [PATCH 138/263] Added providers to metapackage --- src/Discord.Net/Discord.Net.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 4645b3c7e..06113a064 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,4 +1,4 @@ - + An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0-beta2 @@ -22,4 +22,8 @@ + + + + \ No newline at end of file From 64e22947eabcec19fdbba54c48d2345a543dd981 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 1 Jan 2017 23:57:17 -0400 Subject: [PATCH 139/263] Updated README --- README.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index b287d7015..7b4b3a512 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Discord.Net v1.0.0-beta2 +# Discord.Net v1.0.0-rc [![MyGet](https://img.shields.io/myget/discord-net/vpre/Discord.Net.svg)](https://www.myget.org/feed/Packages/discord-net) [![MyGet Build Status](https://www.myget.org/BuildSource/Badge/discord-net?identifier=15bf7c42-22dd-4406-93e5-3cafc62bbc85)](https://www.myget.org/) -[![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/0SBTUU1wZTYLhAAW) +[![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/0SBTUU1wZTVjAMPx) An unofficial .Net API Wrapper for the Discord client (http://discordapp.com). @@ -9,12 +9,19 @@ Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Disc ## Installation ### Stable (NuGet) -Our stable builds are available from NuGet: +Our stable builds available from NuGet through the Discord.Net metapackage: - [Discord.Net](https://www.nuget.org/packages/Discord.Net/) + +The individual components may also be installed from NuGet: +- [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/) +- [Discord.Net.Rpc](https://www.nuget.org/packages/Discord.Net.Rpc/) +- [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/) - [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/) +- [Discord.Net.Providers.UdpClient](https://www.nuget.org/packages/Discord.Net.Providers.UdpClient/) +- [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) ### Unstable (MyGet) -Bleeding edge builds are available using our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`). These builds may break at any time - use with caution. +Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`). ## Compiling In order to compile Discord.Net, you require the following: @@ -30,4 +37,5 @@ The .NET Core and Docker (Preview) workload is required during Visual Studio ins ## Known Issues ### WebSockets (Win7 and earlier) -.Net Core 1.1 does not support WebSockets on Win7 and earlier. Track the issue [here](https://github.com/dotnet/corefx/issues/9503). +.Net Core 1.1 does not support WebSockets on Win7 and earlier. It's recommended to use the Discord.Net.Providers.WS4Net package until this is resolved. +Track the issue [here](https://github.com/dotnet/corefx/issues/9503). From d06b7856f630ec2ac4b73ee7e8e3cd47d34f928b Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 2 Jan 2017 00:05:45 -0400 Subject: [PATCH 140/263] Fixed several package errors --- README.md | 2 ++ src/Discord.Net.Commands/Discord.Net.Commands.csproj | 4 ++-- src/Discord.Net.Core/Discord.Net.Core.csproj | 4 ++-- src/Discord.Net.Core/project.json | 3 --- .../Discord.Net.Providers.UdpClient.csproj | 2 +- .../Discord.Net.Providers.WS4Net.csproj | 2 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 2 +- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 2 +- src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj | 4 ++-- src/Discord.Net/Discord.Net.csproj | 6 +----- 10 files changed, 13 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7b4b3a512..934cb775c 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ The individual components may also be installed from NuGet: - [Discord.Net.Rpc](https://www.nuget.org/packages/Discord.Net.Rpc/) - [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/) - [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/) + +The following providers are available for platforms not supporting .NET Standard 1.3: - [Discord.Net.Providers.UdpClient](https://www.nuget.org/packages/Discord.Net.Providers.UdpClient/) - [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 643bac7bc..099472a34 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,7 +1,7 @@ - + A Discord.Net extension adding support for bot commands. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 238f3e4e2..f3aeac0c0 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,7 +1,7 @@ - + A .Net API wrapper and bot framework for Discord. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json index 86caebfc1..54cd9ffda 100644 --- a/src/Discord.Net.Core/project.json +++ b/src/Discord.Net.Core/project.json @@ -26,9 +26,6 @@ }, "dependencies": { - "Discord.Net.API": { - "target": "project" - }, "Newtonsoft.Json": "9.0.1", "System.Collections.Concurrent": "4.3.0", "System.Collections.Immutable": "1.3.0", diff --git a/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj index 64225a50d..cf542943a 100644 --- a/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj @@ -1,7 +1,7 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient - 1.0.0-beta2 + 1.0.0-rc net45 true Discord.Net.Providers.UDPClient diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index d1c44c11c..ee267a2c3 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -1,7 +1,7 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net - 1.0.0-beta2 + 1.0.0-rc net45 true Discord.Net.Providers.WS4Net diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 8fc1eb274..753428943 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,7 +1,7 @@  A core Discord.Net library containing the REST client and models. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 12b77e5bc..9ba8e338f 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,7 +1,7 @@  A core Discord.Net library containing the RPC client and models. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 0887e3554..03857dbe8 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,7 +1,7 @@ - + A core Discord.Net library containing the WebSocket client and models. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 true Discord.Net.WebSocket diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 06113a064..4a01ef042 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,7 +1,7 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. - 1.0.0-beta2 + 1.0.0-rc netstandard1.1;netstandard1.3 Discord.Net discord;discordapp @@ -22,8 +22,4 @@ - - - - \ No newline at end of file From cf2596db5881f67b0e1ea8b46404521a7cc34882 Mon Sep 17 00:00:00 2001 From: RogueException Date: Mon, 2 Jan 2017 01:17:32 -0400 Subject: [PATCH 141/263] Fixed a nullref when an embed is not provided to SendMessage --- src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 9126ace5c..5ddf044c6 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -152,7 +152,7 @@ namespace Discord.Rest public static async Task SendMessageAsync(IMessageChannel channel, BaseDiscordClient client, string text, bool isTTS, Embed embed, RequestOptions options) { - var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed.ToModel() }; + var args = new CreateMessageParams(text) { IsTTS = isTTS, Embed = embed?.ToModel() }; var model = await client.ApiClient.CreateMessageAsync(channel.Id, args, options).ConfigureAwait(false); return RestUserMessage.Create(client, channel, client.CurrentUser, model); } From a03269fcf1265f766709398eaafbe481f997db53 Mon Sep 17 00:00:00 2001 From: Sentinent Date: Thu, 5 Jan 2017 01:44:41 -0800 Subject: [PATCH 142/263] Fixed guild member's joinedat reseting after certain events --- src/Discord.Net.Rest/API/Common/GuildMember.cs | 2 +- src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs | 3 ++- src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Rest/API/Common/GuildMember.cs b/src/Discord.Net.Rest/API/Common/GuildMember.cs index ba3d05462..daba36d23 100644 --- a/src/Discord.Net.Rest/API/Common/GuildMember.cs +++ b/src/Discord.Net.Rest/API/Common/GuildMember.cs @@ -13,7 +13,7 @@ namespace Discord.API [JsonProperty("roles")] public ulong[] Roles { get; set; } [JsonProperty("joined_at")] - public DateTimeOffset JoinedAt { get; set; } + public Optional JoinedAt { get; set; } [JsonProperty("deaf")] public bool Deaf { get; set; } [JsonProperty("mute")] diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index ee3551fd5..af24c7bb2 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -47,7 +47,8 @@ namespace Discord.Rest } internal void Update(Model model) { - _joinedAtTicks = model.JoinedAt.UtcTicks; + if (model.JoinedAt.IsSpecified) + _joinedAtTicks = model.JoinedAt.Value.UtcTicks; if (model.Nick.IsSpecified) Nickname = model.Nick.Value; IsDeafened = model.Deaf; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 720f39c04..3e6c44a4d 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -80,7 +80,8 @@ namespace Discord.WebSocket internal void Update(ClientState state, Model model) { base.Update(state, model.User); - _joinedAtTicks = model.JoinedAt.UtcTicks; + if (model.JoinedAt.IsSpecified) + _joinedAtTicks = model.JoinedAt.Value.UtcTicks; if (model.Nick.IsSpecified) Nickname = model.Nick.Value; UpdateRoles(model.Roles); From 6dc52e8eb8b9e41b1b7247ffa20a2e415359a3c8 Mon Sep 17 00:00:00 2001 From: Sentinent Date: Fri, 6 Jan 2017 00:42:16 -0800 Subject: [PATCH 143/263] Fixed users not being updated on PRESENCE_UPDATEs --- src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index b94da2438..393bc22b5 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -40,6 +40,7 @@ namespace Discord.WebSocket internal virtual void Update(ClientState state, PresenceModel model) { Presence = SocketPresence.Create(model); + Update(state, model.User); } public Task CreateDMChannelAsync(RequestOptions options = null) From 4188a274018638db1994e460b8e0c323863aaa02 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 14 Jan 2017 18:49:40 -0500 Subject: [PATCH 144/263] Unparsed -> Remainder in example. --- docs/guides/samples/module.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/samples/module.cs b/docs/guides/samples/module.cs index 66d2907df..403acba06 100644 --- a/docs/guides/samples/module.cs +++ b/docs/guides/samples/module.cs @@ -7,7 +7,7 @@ public class Info : ModuleBase { // ~say hello -> hello [Command("say"), Summary("Echos a message.")] - public async Task Say([Unparsed, Summary("The text to echo")] string echo) + public async Task Say([Remainder, Summary("The text to echo")] string echo) { // ReplyAsync is a method on ModuleBase await ReplyAsync(echo); @@ -39,4 +39,4 @@ public class Sample : ModuleBase var userInfo = user ?? Context.Client.CurrentUser; await ReplyAsync($"{userInfo.Username}#{userInfo.Discriminator}"); } -} \ No newline at end of file +} From 236304c1a4d7c21c239d29ec500edd876a412b48 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 14 Jan 2017 19:09:21 -0500 Subject: [PATCH 145/263] Include command contexts on docs --- docs/guides/commands.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/guides/commands.md b/docs/guides/commands.md index a0c6f83c0..1093b7937 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -61,7 +61,7 @@ By now, your module should look like this: [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx -[ModuleBase]: xref:Discord.Commands.ModuleBase +[ModuleBase]: xref:Discord.Commands.ModuleBase-1 ### Adding Commands @@ -117,12 +117,21 @@ property on [ModuleBase]. CommandContext allows you to access the message, channel, guild, and user that the command was invoked from, as well as the underlying discord client the command was invoked from. -You may need to cast these objects to their Socket counterparts (see -the terminology section). +Different types of Contexts may be specified using the generic variant +of [ModuleBase]. When using a [SocketCommandContext], for example, +the properties on this context will already be Socket entities. You +will not need to cast them. To reply to messages, you may also invoke [ReplyAsync], instead of accessing the channel through the [Context] and sending a message. +[Context]: xref:Discord.Commands.ModuleBase-1#Discord_Commands_ModuleBase_1_Context +[SocketCommandContext]: Discord.Commands.SocketCommandContext + +>![WARNING] +>Contexts should **NOT** be mixed! You cannot have one module that +>uses CommandContext, and another that uses SocketCommandContext. + ### Example Module At this point, your module should look comparable to this example: From c2b50b56d8263137682009ada50f5c5d13e578d9 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sun, 15 Jan 2017 21:19:08 +0000 Subject: [PATCH 146/263] Ensure aliases are built in the correct order Fixes #462 --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 40a434592..4253b0bee 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -40,16 +40,17 @@ namespace Discord.Commands private static IEnumerable BuildAliases(ModuleBuilder builder, CommandService service) { var result = builder.Aliases.ToList(); - var builderStack = new Stack(); + var builderQueue = new Queue(); var parent = builder; while ((parent = parent.Parent) != null) - builderStack.Push(parent); + builderQueue.Enqueue(parent); - while (builderStack.Count > 0) + while (builderQueue.Count > 0) { - var level = builderStack.Pop(); - result = result.Permutate(level.Aliases, (first, second) => + var level = builderQueue.Dequeue(); + // permute in reverse because we want to *prefix* our aliases + result = level.Aliases.Permutate(result, (first, second) => { if (first == "") return second; From a65ba095affcdf592d487e4cd6cf30fb0d8a5371 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Tue, 17 Jan 2017 20:08:18 +0000 Subject: [PATCH 147/263] Replace ConcurrentBag with HashSet Fixes #487 --- src/Discord.Net.Commands/CommandService.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 0d27bd178..02a398851 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -18,7 +18,7 @@ namespace Discord.Commands private readonly ConcurrentDictionary> _typeReaders; private readonly ConcurrentDictionary _defaultTypeReaders; private readonly ImmutableList> _entityTypeReaders; //TODO: Candidate for C#7 Tuple - private readonly ConcurrentBag _moduleDefs; + private readonly HashSet _moduleDefs; private readonly CommandMap _map; internal readonly bool _caseSensitive; @@ -160,8 +160,7 @@ namespace Discord.Commands } private bool RemoveModuleInternal(ModuleInfo module) { - var defsRemove = module; - if (!_moduleDefs.TryTake(out defsRemove)) + if (!_moduleDefs.Remove(module)) return false; foreach (var cmd in module.Commands) From 0715cb6623feb9540c0ee68f7982e2d4e3ade19b Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sun, 22 Jan 2017 03:08:52 +0000 Subject: [PATCH 148/263] Actually use HashSet to initialize _moduleDefs --- src/Discord.Net.Commands/CommandService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 02a398851..9f6e66c20 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -38,7 +38,7 @@ namespace Discord.Commands _moduleLock = new SemaphoreSlim(1, 1); _typedModuleDefs = new ConcurrentDictionary(); - _moduleDefs = new ConcurrentBag(); + _moduleDefs = new HashSet(); _map = new CommandMap(this); _typeReaders = new ConcurrentDictionary>(); From c1e445d8ce2c1ac31c780142cda561d409159421 Mon Sep 17 00:00:00 2001 From: Confruggy Date: Sun, 22 Jan 2017 22:33:03 +0100 Subject: [PATCH 149/263] Update Format.cs --- src/Discord.Net.Core/Format.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Format.cs b/src/Discord.Net.Core/Format.cs index 0039836d8..df4e13f77 100644 --- a/src/Discord.Net.Core/Format.cs +++ b/src/Discord.Net.Core/Format.cs @@ -3,7 +3,7 @@ public static class Format { // Characters which need escaping - private static string[] SensitiveCharacters = { "*", "_", "~", "`", "\\" }; + private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`", }; /// Returns a markdown-formatted string with bold formatting. public static string Bold(string text) => $"**{text}**"; From a59fa722e7b88c5b32641d563db5fe544ea69507 Mon Sep 17 00:00:00 2001 From: Flamanis Date: Sun, 22 Jan 2017 18:11:10 -0600 Subject: [PATCH 150/263] Update logging.cs --- docs/guides/samples/logging.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 5155bacdc..15dc773f1 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -4,12 +4,12 @@ using Discord.Rest; public class Program { // Note: This is the light client, it only supports REST calls. - private DiscordClient _client; + private DiscordSocketClient _client; static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); public async Task Start() { - _client = new DiscordClient(new DiscordConfig() { + _client = new DiscordSocketClient(new DiscordConfig() { LogLevel = LogSeverity.Info }); @@ -20,5 +20,8 @@ public class Program }; await _client.LoginAsync(TokenType.Bot, "bot token"); + await _client.ConnectAsync(); + + await Task.Delay(-1); } -} \ No newline at end of file +} From 2b9cedd9dfd515edd35d29a298c59e04fa026dac Mon Sep 17 00:00:00 2001 From: Flamanis Date: Sun, 22 Jan 2017 18:14:33 -0600 Subject: [PATCH 151/263] Update logging.cs --- docs/guides/samples/logging.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 15dc773f1..ebe824437 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -9,7 +9,7 @@ public class Program public async Task Start() { - _client = new DiscordSocketClient(new DiscordConfig() { + _client = new DiscordSocketClient(new DiscordSocketConfig() { LogLevel = LogSeverity.Info }); From e907c3e500d6f200dbeb331657b370ec8f46ddd4 Mon Sep 17 00:00:00 2001 From: Flamanis Date: Sun, 22 Jan 2017 18:21:28 -0600 Subject: [PATCH 152/263] Update logging.cs --- docs/guides/samples/logging.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index ebe824437..5de5e8263 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -3,7 +3,6 @@ using Discord.Rest; public class Program { - // Note: This is the light client, it only supports REST calls. private DiscordSocketClient _client; static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); From 1c258544ba07f249c58c58e3cfcf32f0806422fd Mon Sep 17 00:00:00 2001 From: Flamanis Date: Sun, 22 Jan 2017 18:25:12 -0600 Subject: [PATCH 153/263] Update logging.cs --- docs/guides/samples/logging.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 5de5e8263..4c47d1ea8 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -18,7 +18,7 @@ public class Program return Task.CompletedTask; }; - await _client.LoginAsync(TokenType.Bot, "bot token"); + await _client.LoginAsync(TokenType.Bot, "bot token"); await _client.ConnectAsync(); await Task.Delay(-1); From aed63e67f6f1d7caaa2b45afe2601b35d9eaabbb Mon Sep 17 00:00:00 2001 From: Flamanis Date: Sun, 22 Jan 2017 18:25:52 -0600 Subject: [PATCH 154/263] Update logging.cs --- docs/guides/samples/logging.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 4c47d1ea8..05d8e9496 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -18,9 +18,9 @@ public class Program return Task.CompletedTask; }; - await _client.LoginAsync(TokenType.Bot, "bot token"); - await _client.ConnectAsync(); + await _client.LoginAsync(TokenType.Bot, "bot token"); + await _client.ConnectAsync(); - await Task.Delay(-1); + await Task.Delay(-1); } } From 02b07263382a4111fe5a5ba750f6099692539055 Mon Sep 17 00:00:00 2001 From: Confruggy Date: Mon, 23 Jan 2017 16:10:56 +0100 Subject: [PATCH 155/263] Update Format.cs removed comma and space --- src/Discord.Net.Core/Format.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Format.cs b/src/Discord.Net.Core/Format.cs index df4e13f77..aa822f99e 100644 --- a/src/Discord.Net.Core/Format.cs +++ b/src/Discord.Net.Core/Format.cs @@ -3,7 +3,7 @@ public static class Format { // Characters which need escaping - private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`", }; + private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`" }; /// Returns a markdown-formatted string with bold formatting. public static string Bold(string text) => $"**{text}**"; From 5c4672bc246e72cfa62344a8cd9814b79aa4df97 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:33:20 -0400 Subject: [PATCH 156/263] Added db files to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d7bf0ef19..2bb9bf8fe 100644 --- a/.gitignore +++ b/.gitignore @@ -198,6 +198,7 @@ FakesAssemblies/ #Custom project.lock.json /test/Discord.Net.Tests/config.json +/test/Discord.Net.Tests/cache.db* /docs/_build *.pyc -/.editorconfig +/.editorconfig \ No newline at end of file From 62d0017e0482404d85b12f797670af18da54dfff Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:34:10 -0400 Subject: [PATCH 157/263] Added .vscode settings to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2bb9bf8fe..ccd272109 100644 --- a/.gitignore +++ b/.gitignore @@ -201,4 +201,5 @@ project.lock.json /test/Discord.Net.Tests/cache.db* /docs/_build *.pyc -/.editorconfig \ No newline at end of file +/.editorconfig +.vscode/ \ No newline at end of file From 00bb3c9d7019f0b64eb860b6c8ddbf75de184f6f Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:35:36 -0400 Subject: [PATCH 158/263] Removed project.jsons --- src/Discord.Net.Commands/project.json | 38 ------------- src/Discord.Net.Core/project.json | 42 --------------- .../project.json | 37 ------------- src/Discord.Net.Providers.WS4Net/project.json | 38 ------------- src/Discord.Net.Rest/project.json | 42 --------------- src/Discord.Net.Rpc/project.json | 52 ------------------ src/Discord.Net.WebSocket/project.json | 53 ------------------- src/Discord.Net/project.json | 38 ------------- 8 files changed, 340 deletions(-) delete mode 100644 src/Discord.Net.Commands/project.json delete mode 100644 src/Discord.Net.Core/project.json delete mode 100644 src/Discord.Net.Providers.UDPClient/project.json delete mode 100644 src/Discord.Net.Providers.WS4Net/project.json delete mode 100644 src/Discord.Net.Rest/project.json delete mode 100644 src/Discord.Net.Rpc/project.json delete mode 100644 src/Discord.Net.WebSocket/project.json delete mode 100644 src/Discord.Net/project.json diff --git a/src/Discord.Net.Commands/project.json b/src/Discord.Net.Commands/project.json deleted file mode 100644 index d918d0e7e..000000000 --- a/src/Discord.Net.Commands/project.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A Discord.Net extension adding support for bot commands.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - } - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": {} - } -} \ No newline at end of file diff --git a/src/Discord.Net.Core/project.json b/src/Discord.Net.Core/project.json deleted file mode 100644 index 54cd9ffda..000000000 --- a/src/Discord.Net.Core/project.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A .Net API wrapper and bot framework for Discord.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Newtonsoft.Json": "9.0.1", - "System.Collections.Concurrent": "4.3.0", - "System.Collections.Immutable": "1.3.0", - "System.Interactive.Async": "3.1.0", - "System.Net.Http": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.1.1" - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": {} - } -} diff --git a/src/Discord.Net.Providers.UDPClient/project.json b/src/Discord.Net.Providers.UDPClient/project.json deleted file mode 100644 index 59d8509ad..000000000 --- a/src/Discord.Net.Providers.UDPClient/project.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "An optional UDP client provider for Discord.Net using System.Net.UdpClient.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - } - }, - - "frameworks": { - "net45": {} - } -} \ No newline at end of file diff --git a/src/Discord.Net.Providers.WS4Net/project.json b/src/Discord.Net.Providers.WS4Net/project.json deleted file mode 100644 index 4e35d6bbb..000000000 --- a/src/Discord.Net.Providers.WS4Net/project.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "An optional WebSocket client provider for Discord.Net using WebSocket4Net.", - "authors": [ "RogueException", "foxbot" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - }, - "WebSocket4Net": "0.14.1" - }, - - "frameworks": { - "net45": {} - } -} \ No newline at end of file diff --git a/src/Discord.Net.Rest/project.json b/src/Discord.Net.Rest/project.json deleted file mode 100644 index 7db631b18..000000000 --- a/src/Discord.Net.Rest/project.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A core Discord.Net library containing the REST client and models.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - }, - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": { - "dependencies": { - "System.IO.FileSystem": "4.3.0" - } - } - } -} \ No newline at end of file diff --git a/src/Discord.Net.Rpc/project.json b/src/Discord.Net.Rpc/project.json deleted file mode 100644 index a638f4588..000000000 --- a/src/Discord.Net.Rpc/project.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A core Discord.Net library containing the RPC client and models.", - "authors": [ "RogueException" ], - - "buildOptions": { - "compile": { - "includeFiles": [ "../Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs" ] - } - }, - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - }, - "Discord.Net.Rest": { - "target": "project" - }, - "System.IO.Compression": "4.3.0" - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": { - "dependencies": { - "System.Net.WebSockets.Client": "4.3.0" - } - } - } -} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/project.json b/src/Discord.Net.WebSocket/project.json deleted file mode 100644 index 4ee07e2ac..000000000 --- a/src/Discord.Net.WebSocket/project.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "A core Discord.Net library containing the WebSocket client and models.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "buildOptions": { - "allowUnsafe": true - }, - - "configurations": { - "Release": { - "buildOptions": { - "define": [ "RELEASE" ], - "nowarn": [ "CS1573", "CS1591" ], - "optimize": true, - "warningsAsErrors": true, - "xmlDoc": true - } - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - }, - "Discord.Net.Rest": { - "target": "project" - }, - "System.IO.Compression": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": { - "dependencies": { - "System.Net.NameResolution": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Net.WebSockets.Client": "4.3.0" - } - } - } -} \ No newline at end of file diff --git a/src/Discord.Net/project.json b/src/Discord.Net/project.json deleted file mode 100644 index c02f510f9..000000000 --- a/src/Discord.Net/project.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "version": "1.0.0-*", - "description": "An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components.", - "authors": [ "RogueException" ], - - "packOptions": { - "tags": [ "discord", "discordapp" ], - "licenseUrl": "http://opensource.org/licenses/MIT", - "projectUrl": "https://github.com/RogueException/Discord.Net", - "repository": { - "type": "git", - "url": "git://github.com/RogueException/Discord.Net" - } - }, - - "dependencies": { - "Discord.Net.Core": { - "target": "project" - }, - "Discord.Net.Rest": { - "target": "project" - }, - "Discord.Net.WebSocket": { - "target": "project" - }, - "Discord.Net.Rpc": { - "target": "project" - }, - "Discord.Net.Commands": { - "target": "project" - } - }, - - "frameworks": { - "netstandard1.1": {}, - "netstandard1.3": {} - } -} \ No newline at end of file From 69bd4b4ea65495dab890fdddacdb1244e99c6ef2 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:36:07 -0400 Subject: [PATCH 159/263] Updated .NET dependencies to 4.3.0 --- src/Discord.Net.Core/Discord.Net.Core.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index f3aeac0c0..582f34d80 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -20,13 +20,13 @@ - - + + - + - - + + $(NoWarn);CS1573;CS1591 From ea0044cb872d80dadd552854e0ea185680ccdb5b Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:37:39 -0400 Subject: [PATCH 160/263] Made IVoiceChannel.UserLimit nullable --- src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs | 4 ++-- .../Entities/Channels/VoiceChannelProperties.cs | 2 +- src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 2 +- src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs | 7 +++---- src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs | 7 +++---- .../Entities/Channels/SocketVoiceChannel.cs | 7 +++---- 6 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs index d25576c65..80c90e4bd 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -8,8 +8,8 @@ namespace Discord { /// Gets the bitrate, in bits per second, clients in this voice channel are requested to use. int Bitrate { get; } - /// Gets the max amount of users allowed to be connected to this channel at one time. A value of 0 represents no limit. - int UserLimit { get; } + /// Gets the max amount of users allowed to be connected to this channel at one time. + int? UserLimit { get; } /// Modifies this voice channel. Task ModifyAsync(Action func, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs b/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs index 3294ad146..81dd8063e 100644 --- a/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs +++ b/src/Discord.Net.Core/Entities/Channels/VoiceChannelProperties.cs @@ -10,6 +10,6 @@ /// /// The maximum number of users that can be present in a channel. /// - public Optional UserLimit { get; set; } + public Optional UserLimit { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 5ddf044c6..b7d81c579 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -56,7 +56,7 @@ namespace Discord.Rest Bitrate = args.Bitrate, Name = args.Name, Position = args.Position, - UserLimit = args.UserLimit + UserLimit = args.UserLimit.IsSpecified ? (args.UserLimit.Value ?? 0) : Optional.Create() }; return await client.ApiClient.ModifyGuildChannelAsync(channel.Id, apiArgs, options).ConfigureAwait(false); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index dd4f59deb..e5330f29e 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Diagnostics; @@ -13,7 +12,7 @@ namespace Discord.Rest public class RestVoiceChannel : RestGuildChannel, IVoiceChannel, IRestAudioChannel { public int Bitrate { get; private set; } - public int UserLimit { get; private set; } + public int? UserLimit { get; private set; } internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id) : base(discord, guild, id) @@ -30,7 +29,7 @@ namespace Discord.Rest base.Update(model); Bitrate = model.Bitrate.Value; - UserLimit = model.UserLimit.Value; + UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } public async Task ModifyAsync(Action func, RequestOptions options = null) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs index ed05e2f57..3d5acfda9 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcVoiceChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using Discord.Rest; using System; using System.Collections.Generic; @@ -14,8 +13,8 @@ namespace Discord.Rpc [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class RpcVoiceChannel : RpcGuildChannel, IRpcAudioChannel, IVoiceChannel { - public int UserLimit { get; private set; } public int Bitrate { get; private set; } + public int? UserLimit { get; private set; } public IReadOnlyCollection VoiceStates { get; private set; } internal RpcVoiceChannel(DiscordRpcClient discord, ulong id, ulong guildId) @@ -32,7 +31,7 @@ namespace Discord.Rpc { base.Update(model); if (model.UserLimit.IsSpecified) - UserLimit = model.UserLimit.Value; + UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; if (model.Bitrate.IsSpecified) Bitrate = model.Bitrate.Value; VoiceStates = model.VoiceStates.Select(x => RpcVoiceState.Create(Discord, x)).ToImmutableArray(); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index 072ccc787..71017a7c8 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using Discord.Rest; using System; using System.Collections.Generic; @@ -15,7 +14,7 @@ namespace Discord.WebSocket public class SocketVoiceChannel : SocketGuildChannel, IVoiceChannel, ISocketAudioChannel { public int Bitrate { get; private set; } - public int UserLimit { get; private set; } + public int? UserLimit { get; private set; } public override IReadOnlyCollection Users => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); @@ -35,7 +34,7 @@ namespace Discord.WebSocket base.Update(state, model); Bitrate = model.Bitrate.Value; - UserLimit = model.UserLimit.Value; + UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } public Task ModifyAsync(Action func, RequestOptions options = null) From b76c744447e3a5ce8b710ab009eb0d0c959e0b3a Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:38:53 -0400 Subject: [PATCH 161/263] Internally exposed several ApiClient props --- src/Discord.Net.Rest/DiscordRestApiClient.cs | 60 +++++++++---------- .../DiscordSocketApiClient.cs | 33 +++++----- .../DiscordVoiceApiClient.cs | 20 +++---- 3 files changed, 57 insertions(+), 56 deletions(-) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 66021db31..8eec66ec2 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -31,12 +31,10 @@ namespace Discord.API protected readonly JsonSerializer _serializer; protected readonly SemaphoreSlim _stateLock; - private readonly RestClientProvider _restClientProvider; + private readonly RestClientProvider RestClientProvider; - protected string _authToken; protected bool _isDisposed; private CancellationTokenSource _loginCancelToken; - private IRestClient _restClient; private bool _fetchCurrentUser; public RetryMode DefaultRetryMode { get; } @@ -45,6 +43,8 @@ namespace Discord.API public LoginState LoginState { get; private set; } public TokenType AuthTokenType { get; private set; } + internal string AuthToken { get; private set; } + internal IRestClient RestClient { get; private set; } internal User CurrentUser { get; private set; } public ulong? CurrentUserId => CurrentUser?.Id; @@ -52,7 +52,7 @@ namespace Discord.API public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null, bool fetchCurrentUser = true) { - _restClientProvider = restClientProvider; + RestClientProvider = restClientProvider; UserAgent = userAgent; DefaultRetryMode = defaultRetryMode; _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; @@ -65,10 +65,10 @@ namespace Discord.API } internal void SetBaseUrl(string baseUrl) { - _restClient = _restClientProvider(baseUrl); - _restClient.SetHeader("accept", "*/*"); - _restClient.SetHeader("user-agent", UserAgent); - _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); + RestClient = RestClientProvider(baseUrl); + RestClient.SetHeader("accept", "*/*"); + RestClient.SetHeader("user-agent", UserAgent); + RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); } internal static string GetPrefixedToken(TokenType tokenType, string token) { @@ -91,7 +91,7 @@ namespace Discord.API if (disposing) { _loginCancelToken?.Dispose(); - (_restClient as IDisposable)?.Dispose(); + (RestClient as IDisposable)?.Dispose(); } _isDisposed = true; } @@ -118,13 +118,13 @@ namespace Discord.API _loginCancelToken = new CancellationTokenSource(); AuthTokenType = TokenType.User; - _authToken = null; + AuthToken = null; await RequestQueue.SetCancelTokenAsync(_loginCancelToken.Token).ConfigureAwait(false); - _restClient.SetCancelToken(_loginCancelToken.Token); + RestClient.SetCancelToken(_loginCancelToken.Token); AuthTokenType = tokenType; - _authToken = token; - _restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken)); + AuthToken = token; + RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); if (_fetchCurrentUser) CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); @@ -160,7 +160,7 @@ namespace Discord.API await RequestQueue.ClearAsync().ConfigureAwait(false); await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); - _restClient.SetCancelToken(CancellationToken.None); + RestClient.SetCancelToken(CancellationToken.None); CurrentUser = null; LoginState = LoginState.LoggedOut; @@ -181,7 +181,7 @@ namespace Discord.API options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - var request = new RestRequest(_restClient, method, endpoint, options); + var request = new RestRequest(RestClient, method, endpoint, options); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); } @@ -197,7 +197,7 @@ namespace Discord.API options.IsClientBucket = AuthTokenType == TokenType.User; var json = payload != null ? SerializeJson(payload) : null; - var request = new JsonRestRequest(_restClient, method, endpoint, json, options); + var request = new JsonRestRequest(RestClient, method, endpoint, json, options); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); } @@ -212,7 +212,7 @@ namespace Discord.API options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options); + var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); await SendInternalAsync(method, endpoint, request).ConfigureAwait(false); } @@ -226,7 +226,7 @@ namespace Discord.API options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - var request = new RestRequest(_restClient, method, endpoint, options); + var request = new RestRequest(RestClient, method, endpoint, options); return DeserializeJson(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); } @@ -241,7 +241,7 @@ namespace Discord.API options.IsClientBucket = AuthTokenType == TokenType.User; var json = payload != null ? SerializeJson(payload) : null; - var request = new JsonRestRequest(_restClient, method, endpoint, json, options); + var request = new JsonRestRequest(RestClient, method, endpoint, json, options); return DeserializeJson(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); } @@ -255,7 +255,7 @@ namespace Discord.API options.BucketId = AuthTokenType == TokenType.User ? ClientBucket.Get(clientBucket).Id : bucketId; options.IsClientBucket = AuthTokenType == TokenType.User; - var request = new MultipartRestRequest(_restClient, method, endpoint, multipartArgs, options); + var request = new MultipartRestRequest(RestClient, method, endpoint, multipartArgs, options); return DeserializeJson(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false)); } @@ -294,7 +294,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendAsync("GET", () => $"channels/{channelId}", ids, options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task GetChannelAsync(ulong guildId, ulong channelId, RequestOptions options = null) { @@ -310,7 +310,7 @@ namespace Discord.API return null; return model; } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task> GetGuildChannelsAsync(ulong guildId, RequestOptions options = null) { @@ -365,8 +365,8 @@ namespace Discord.API { Preconditions.NotEqual(channelId, 0, nameof(channelId)); Preconditions.NotNull(args, nameof(args)); - Preconditions.GreaterThan(args.Bitrate, 8000, nameof(args.Bitrate)); - Preconditions.AtLeast(args.UserLimit, 0, nameof(args.Bitrate)); + Preconditions.AtLeast(args.Bitrate, 8000, nameof(args.Bitrate)); + Preconditions.AtLeast(args.UserLimit, 0, nameof(args.UserLimit)); Preconditions.AtLeast(args.Position, 0, nameof(args.Position)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); options = RequestOptions.CreateOrClone(options); @@ -407,7 +407,7 @@ namespace Discord.API var ids = new BucketIds(channelId: channelId); return await SendAsync("GET", () => $"channels/{channelId}/messages/{messageId}", ids, options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task> GetChannelMessagesAsync(ulong channelId, GetChannelMessagesParams args, RequestOptions options = null) { @@ -676,7 +676,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync("GET", () => $"guilds/{guildId}", ids, options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task CreateGuildAsync(CreateGuildParams args, RequestOptions options = null) { @@ -779,7 +779,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync("GET", () => $"guilds/{guildId}/embed", ids, options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task ModifyGuildEmbedAsync(ulong guildId, Rest.ModifyGuildEmbedParams args, RequestOptions options = null) { @@ -859,7 +859,7 @@ namespace Discord.API { return await SendAsync("GET", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task> GetGuildInvitesAsync(ulong guildId, RequestOptions options = null) { @@ -915,7 +915,7 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync("GET", () => $"guilds/{guildId}/members/{userId}", ids, options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } public async Task> GetGuildMembersAsync(ulong guildId, GetGuildMembersParams args, RequestOptions options = null) { @@ -1032,7 +1032,7 @@ namespace Discord.API { return await SendAsync("GET", () => $"users/{userId}", new BucketIds(), options: options).ConfigureAwait(false); } - catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.NotFound) { return null; } + catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } //Current User/DMs diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index d5fbcc71d..90f7f5c67 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -26,9 +26,10 @@ namespace Discord.API public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); - private readonly IWebSocketClient _gatewayClient; private CancellationTokenSource _connectCancelToken; private string _gatewayUrl; + + internal IWebSocketClient WebSocketClient { get; } public ConnectionState ConnectionState { get; private set; } @@ -36,9 +37,9 @@ namespace Discord.API RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) : base(restClientProvider, userAgent, defaultRetryMode, serializer, true) { - _gatewayClient = webSocketProvider(); - //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) - _gatewayClient.BinaryMessage += async (data, index, count) => + WebSocketClient = webSocketProvider(); + //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) + WebSocketClient.BinaryMessage += async (data, index, count) => { using (var compressed = new MemoryStream(data, index + 2, count - 2)) using (var decompressed = new MemoryStream()) @@ -54,7 +55,7 @@ namespace Discord.API } } }; - _gatewayClient.TextMessage += async text => + WebSocketClient.TextMessage += async text => { using (var reader = new StringReader(text)) using (var jsonReader = new JsonTextReader(reader)) @@ -63,7 +64,7 @@ namespace Discord.API await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false); } }; - _gatewayClient.Closed += async ex => + WebSocketClient.Closed += async ex => { await DisconnectAsync().ConfigureAwait(false); await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); @@ -76,7 +77,7 @@ namespace Discord.API if (disposing) { _connectCancelToken?.Dispose(); - (_gatewayClient as IDisposable)?.Dispose(); + (WebSocketClient as IDisposable)?.Dispose(); } _isDisposed = true; } @@ -95,22 +96,22 @@ namespace Discord.API { if (LoginState != LoginState.LoggedIn) throw new InvalidOperationException("You must log in before connecting."); - if (_gatewayClient == null) + if (WebSocketClient == null) throw new NotSupportedException("This client is not configured with websocket support."); ConnectionState = ConnectionState.Connecting; try { _connectCancelToken = new CancellationTokenSource(); - if (_gatewayClient != null) - _gatewayClient.SetCancelToken(_connectCancelToken.Token); + if (WebSocketClient != null) + WebSocketClient.SetCancelToken(_connectCancelToken.Token); if (_gatewayUrl == null) { var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false); _gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}"; } - await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false); + await WebSocketClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false); ConnectionState = ConnectionState.Connected; } @@ -142,7 +143,7 @@ namespace Discord.API } internal override async Task DisconnectInternalAsync() { - if (_gatewayClient == null) + if (WebSocketClient == null) throw new NotSupportedException("This client is not configured with websocket support."); if (ConnectionState == ConnectionState.Disconnected) return; @@ -151,7 +152,7 @@ namespace Discord.API try { _connectCancelToken?.Cancel(false); } catch { } - await _gatewayClient.DisconnectAsync().ConfigureAwait(false); + await WebSocketClient.DisconnectAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Disconnected; } @@ -168,7 +169,7 @@ namespace Discord.API payload = new SocketFrame { Operation = (int)opCode, Payload = payload }; if (payload != null) bytes = Encoding.UTF8.GetBytes(SerializeJson(payload)); - await RequestQueue.SendAsync(new WebSocketRequest(_gatewayClient, null, bytes, true, options)).ConfigureAwait(false); + await RequestQueue.SendAsync(new WebSocketRequest(WebSocketClient, null, bytes, true, options)).ConfigureAwait(false); await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); } @@ -192,7 +193,7 @@ namespace Discord.API }; var msg = new IdentifyParams() { - Token = _authToken, + Token = AuthToken, Properties = props, LargeThreshold = largeThreshold, UseCompression = useCompression, @@ -207,7 +208,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); var msg = new ResumeParams() { - Token = _authToken, + Token = AuthToken, SessionId = sessionId, Sequence = lastSeq }; diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index c8a857074..27d2a8003 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -38,13 +38,13 @@ namespace Discord.Audio private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); private readonly JsonSerializer _serializer; - private readonly IWebSocketClient _webSocketClient; private readonly SemaphoreSlim _connectionLock; private CancellationTokenSource _connectCancelToken; private IUdpSocket _udp; private bool _isDisposed; public ulong GuildId { get; } + internal IWebSocketClient WebSocketClient { get; } public ConnectionState ConnectionState { get; private set; } internal DiscordVoiceAPIClient(ulong guildId, WebSocketProvider webSocketProvider, UdpSocketProvider udpSocketProvider, JsonSerializer serializer = null) @@ -63,9 +63,9 @@ namespace Discord.Audio await _receivedPacketEvent.InvokeAsync(data).ConfigureAwait(false); }; - _webSocketClient = webSocketProvider(); + WebSocketClient = webSocketProvider(); //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+) - _webSocketClient.BinaryMessage += async (data, index, count) => + WebSocketClient.BinaryMessage += async (data, index, count) => { using (var compressed = new MemoryStream(data, index + 2, count - 2)) using (var decompressed = new MemoryStream()) @@ -80,12 +80,12 @@ namespace Discord.Audio } } }; - _webSocketClient.TextMessage += async text => + WebSocketClient.TextMessage += async text => { var msg = JsonConvert.DeserializeObject(text); await _receivedEvent.InvokeAsync((VoiceOpCode)msg.Operation, msg.Payload).ConfigureAwait(false); }; - _webSocketClient.Closed += async ex => + WebSocketClient.Closed += async ex => { await DisconnectAsync().ConfigureAwait(false); await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); @@ -101,7 +101,7 @@ namespace Discord.Audio { _connectCancelToken?.Dispose(); (_udp as IDisposable)?.Dispose(); - (_webSocketClient as IDisposable)?.Dispose(); + (WebSocketClient as IDisposable)?.Dispose(); } _isDisposed = true; } @@ -114,7 +114,7 @@ namespace Discord.Audio payload = new SocketFrame { Operation = (int)opCode, Payload = payload }; if (payload != null) bytes = Encoding.UTF8.GetBytes(SerializeJson(payload)); - await _webSocketClient.SendAsync(bytes, 0, bytes.Length, true).ConfigureAwait(false); + await WebSocketClient.SendAsync(bytes, 0, bytes.Length, true).ConfigureAwait(false); await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); } public async Task SendAsync(byte[] data, int bytes) @@ -177,8 +177,8 @@ namespace Discord.Audio _connectCancelToken = new CancellationTokenSource(); var cancelToken = _connectCancelToken.Token; - _webSocketClient.SetCancelToken(cancelToken); - await _webSocketClient.ConnectAsync(url).ConfigureAwait(false); + WebSocketClient.SetCancelToken(cancelToken); + await WebSocketClient.ConnectAsync(url).ConfigureAwait(false); _udp.SetCancelToken(cancelToken); await _udp.StartAsync().ConfigureAwait(false); @@ -211,7 +211,7 @@ namespace Discord.Audio //Wait for tasks to complete await _udp.StopAsync().ConfigureAwait(false); - await _webSocketClient.DisconnectAsync().ConfigureAwait(false); + await WebSocketClient.DisconnectAsync().ConfigureAwait(false); ConnectionState = ConnectionState.Disconnected; } From 7ef48c5ce56bd9e5eb8691842adaaedf2d981a2d Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:40:08 -0400 Subject: [PATCH 162/263] Improved discord HTTP exceptions --- src/Discord.Net.Core/Net/HttpException.cs | 30 ++++++++++++++++--- .../Net/Queue/RequestQueue.cs | 4 +-- .../Net/Queue/RequestQueueBucket.cs | 20 ++++++------- src/Discord.Net.Rest/Net/RateLimitInfo.cs | 6 +++- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/Discord.Net.Core/Net/HttpException.cs b/src/Discord.Net.Core/Net/HttpException.cs index d18d81abf..4141979a0 100644 --- a/src/Discord.Net.Core/Net/HttpException.cs +++ b/src/Discord.Net.Core/Net/HttpException.cs @@ -5,14 +5,36 @@ namespace Discord.Net { public class HttpException : Exception { - public HttpStatusCode StatusCode { get; } + public HttpStatusCode HttpCode { get; } + public int? DiscordCode { get; } public string Reason { get; } - public HttpException(HttpStatusCode statusCode, string reason = null) - : base($"The server responded with error {(int)statusCode} ({statusCode}){(reason != null ? $": \"{reason}\"" : "")}") + public HttpException(HttpStatusCode httpCode, int? discordCode = null, string reason = null) + : base(CreateMessage(httpCode, discordCode, reason)) { - StatusCode = statusCode; + HttpCode = httpCode; + DiscordCode = discordCode; Reason = reason; } + + private static string CreateMessage(HttpStatusCode httpCode, int? discordCode = null, string reason = null) + { + string msg; + if (discordCode != null) + { + if (reason != null) + msg = $"The server responded with error {(int)discordCode}: {reason}"; + else + msg = $"The server responded with error {(int)discordCode}: {httpCode}"; + } + else + { + if (reason != null) + msg = $"The server responded with error {(int)httpCode}: {reason}"; + else + msg = $"The server responded with error {(int)httpCode}: {httpCode}"; + } + return msg; + } } } diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs index ef7951765..fce7e3e1b 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueue.cs @@ -91,9 +91,9 @@ namespace Discord.Net.Queue await Task.Delay(millis).ConfigureAwait(false); } } - internal void PauseGlobal(RateLimitInfo info, TimeSpan lag) + internal void PauseGlobal(RateLimitInfo info) { - _waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + lag.TotalMilliseconds); + _waitUntil = DateTimeOffset.UtcNow.AddMilliseconds(info.RetryAfter.Value + (info.Lag?.TotalMilliseconds ?? 0.0)); } private RequestBucket GetOrCreateBucket(string id, RestRequest request) diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 5f23a626f..2cc4b8a10 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -54,12 +54,10 @@ namespace Discord.Net.Queue #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Sending..."); #endif - TimeSpan lag = default(TimeSpan); RateLimitInfo info = default(RateLimitInfo); try { var response = await request.SendAsync().ConfigureAwait(false); - lag = DateTimeOffset.UtcNow - DateTimeOffset.Parse(response.Headers["Date"]); info = new RateLimitInfo(response.Headers); if (response.StatusCode < (HttpStatusCode)200 || response.StatusCode >= (HttpStatusCode)300) @@ -72,14 +70,14 @@ namespace Discord.Net.Queue #if DEBUG_LIMITS Debug.WriteLine($"[{id}] (!) 429 [Global]"); #endif - _queue.PauseGlobal(info, lag); + _queue.PauseGlobal(info); } else { #if DEBUG_LIMITS Debug.WriteLine($"[{id}] (!) 429"); #endif - UpdateRateLimit(id, request, info, lag, true); + UpdateRateLimit(id, request, info, true); } await _queue.RaiseRateLimitTriggered(Id, info).ConfigureAwait(false); continue; //Retry @@ -92,6 +90,7 @@ namespace Discord.Net.Queue continue; //Retry default: + int? code = null; string reason = null; if (response.Stream != null) { @@ -101,12 +100,13 @@ namespace Discord.Net.Queue using (var jsonReader = new JsonTextReader(reader)) { var json = JToken.Load(jsonReader); - reason = json.Value("message"); + try { code = json.Value("code"); } catch { }; + try { reason = json.Value("message"); } catch { }; } } catch { } } - throw new HttpException(response.StatusCode, reason); + throw new HttpException(response.StatusCode, code, reason); } } else @@ -142,7 +142,7 @@ namespace Discord.Net.Queue }*/ finally { - UpdateRateLimit(id, request, info, lag, false); + UpdateRateLimit(id, request, info, false); #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Stop"); #endif @@ -214,7 +214,7 @@ namespace Discord.Net.Queue } } - private void UpdateRateLimit(int id, RestRequest request, RateLimitInfo info, TimeSpan lag, bool is429) + private void UpdateRateLimit(int id, RestRequest request, RateLimitInfo info, bool is429) { if (WindowCount == 0) return; @@ -250,10 +250,10 @@ namespace Discord.Net.Queue } else if (info.Reset.HasValue) { - resetTick = info.Reset.Value.AddSeconds(/*1.0 +*/ lag.TotalSeconds); + resetTick = info.Reset.Value.AddSeconds(info.Lag?.TotalSeconds ?? 1.0); int diff = (int)(resetTick.Value - DateTimeOffset.UtcNow).TotalMilliseconds; #if DEBUG_LIMITS - Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {lag.TotalMilliseconds} ms lag)"); + Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {info.Lag?.TotalMilliseconds} ms lag)"); #endif } else if (request.Options.IsClientBucket && request.Options.BucketId != null) diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index 5e87f664d..d8d168aec 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -10,6 +10,7 @@ namespace Discord.Net public int? Remaining { get; } public int? RetryAfter { get; } public DateTimeOffset? Reset { get; } + public TimeSpan? Lag { get; } internal RateLimitInfo(Dictionary headers) { @@ -17,8 +18,11 @@ namespace Discord.Net IsGlobal = headers.TryGetValue("X-RateLimit-Global", out temp) ? bool.Parse(temp) : false; Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) ? int.Parse(temp) : (int?)null; Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) ? int.Parse(temp) : (int?)null; - Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; + Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) ? + DateTimeUtils.FromUnixSeconds(int.Parse(temp)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) ? int.Parse(temp) : (int?)null; + Lag = headers.TryGetValue("Date", out temp) ? + DateTimeOffset.UtcNow - DateTimeOffset.Parse(temp) : (TimeSpan?)null; } } } From 158222bb78693c94b2f29b7b1918e86c123ac7e3 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:41:07 -0400 Subject: [PATCH 163/263] Merged GuildExtensions into IGuild --- .../Entities/Guilds/IGuild.cs | 9 ++ .../Extensions/GuildExtensions.cs | 38 ------ .../Entities/Guilds/RestGuild.cs | 109 +++++++++++++++++- .../Entities/Guilds/SocketGuild.cs | 55 ++++++++- 4 files changed, 164 insertions(+), 47 deletions(-) delete mode 100644 src/Discord.Net.Core/Extensions/GuildExtensions.cs diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index f62ea080c..2ce9b48d0 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -77,6 +77,13 @@ namespace Discord Task> GetChannelsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// Gets the channel in this guild with the provided id, or null if not found. Task GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + 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 GetVoiceChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + Task GetAFKChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + Task GetDefaultChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + Task GetEmbedChannelAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// Creates a new text channel. Task CreateTextChannelAsync(string name, RequestOptions options = null); /// Creates a new voice channel. @@ -99,6 +106,8 @@ namespace Discord Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// Gets the current user for this guild. Task GetCurrentUserAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// Gets the owner of this guild. + Task GetOwnerAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); /// Downloads all users for this guild if the current list is incomplete. Task DownloadUsersAsync(); /// Removes all users from this guild if they have not logged on in a provided number of days or, if simulate is true, returns the number of users that would be removed. diff --git a/src/Discord.Net.Core/Extensions/GuildExtensions.cs b/src/Discord.Net.Core/Extensions/GuildExtensions.cs deleted file mode 100644 index ea8e58e2b..000000000 --- a/src/Discord.Net.Core/Extensions/GuildExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Discord -{ - public static class GuildExtensions - { - public static async Task GetTextChannelAsync(this IGuild guild, ulong id) - => await guild.GetChannelAsync(id).ConfigureAwait(false) as ITextChannel; - public static async Task> GetTextChannelsAsync(this IGuild guild) - => (await guild.GetChannelsAsync().ConfigureAwait(false)).Select(x => x as ITextChannel).Where(x => x != null); - - public static async Task GetVoiceChannelAsync(this IGuild guild, ulong id) - => await guild.GetChannelAsync(id).ConfigureAwait(false) as IVoiceChannel; - public static async Task> GetVoiceChannelsAsync(this IGuild guild) - => (await guild.GetChannelsAsync().ConfigureAwait(false)).Select(x => x as IVoiceChannel).Where(x => x != null); - - public static async Task GetAFKChannelAsync(this IGuild guild) - { - var afkId = guild.AFKChannelId; - if (afkId.HasValue) - return await guild.GetChannelAsync(afkId.Value).ConfigureAwait(false) as IVoiceChannel; - return null; - } - public static async Task GetDefaultChannelAsync(this IGuild guild) - => await guild.GetChannelAsync(guild.DefaultChannelId).ConfigureAwait(false) as ITextChannel; - public static async Task GetEmbedChannelAsync(this IGuild guild) - { - var embedId = guild.EmbedChannelId; - if (embedId.HasValue) - return await guild.GetChannelAsync(embedId.Value).ConfigureAwait(false) as IVoiceChannel; - return null; - } - public static async Task GetOwnerAsync(this IGuild guild) - => await guild.GetUserAsync(guild.OwnerId).ConfigureAwait(false); - } -} diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 340fe7f3c..0622df6ce 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -152,7 +151,53 @@ 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); + return channel as RestTextChannel; + } + public async Task> GetTextChannelsAsync(RequestOptions options = null) + { + var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); + return channels.Select(x => x as RestTextChannel).Where(x => x != null).ToImmutableArray(); + } + public async Task GetVoiceChannelAsync(ulong id, RequestOptions options = null) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, id, options).ConfigureAwait(false); + return channel as RestVoiceChannel; + } + public async Task> GetVoiceChannelsAsync(RequestOptions options = null) + { + 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 GetAFKChannelAsync(RequestOptions options = null) + { + var afkId = AFKChannelId; + if (afkId.HasValue) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, afkId.Value, options).ConfigureAwait(false); + return channel as RestVoiceChannel; + } + return null; + } + public async Task GetDefaultChannelAsync(RequestOptions options = null) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, DefaultChannelId, options).ConfigureAwait(false); + return channel as RestTextChannel; + } + public async Task GetEmbedChannelAsync(RequestOptions options = null) + { + var embedId = EmbedChannelId; + if (embedId.HasValue) + { + var channel = await GuildHelper.GetChannelAsync(this, Discord, embedId.Value, options).ConfigureAwait(false); + return channel as RestVoiceChannel; + } + return null; + } public Task CreateTextChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) @@ -192,6 +237,8 @@ namespace Discord.Rest => GuildHelper.GetUserAsync(this, Discord, id, options); public Task GetCurrentUserAsync(RequestOptions options = null) => GuildHelper.GetUserAsync(this, Discord, Discord.CurrentUser.Id, options); + public Task GetOwnerAsync(RequestOptions options = null) + => GuildHelper.GetUserAsync(this, Discord, OwnerId, options); public Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null) => GuildHelper.PruneUsersAsync(this, Discord, days, simulate, options); @@ -222,6 +269,55 @@ namespace Discord.Rest else return null; } + async Task> IGuild.GetTextChannelsAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetTextChannelsAsync(options).ConfigureAwait(false); + else + return ImmutableArray.Create(); + } + async Task IGuild.GetTextChannelAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetTextChannelAsync(id, options).ConfigureAwait(false); + else + return null; + } + async Task> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetVoiceChannelsAsync(options).ConfigureAwait(false); + else + return ImmutableArray.Create(); + } + async Task IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetVoiceChannelAsync(id, options).ConfigureAwait(false); + else + return null; + } + async Task IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetAFKChannelAsync(options).ConfigureAwait(false); + else + return null; + } + async Task IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetDefaultChannelAsync(options).ConfigureAwait(false); + else + return null; + } + async Task IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetEmbedChannelAsync(options).ConfigureAwait(false); + else + return null; + } async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options) => await CreateTextChannelAsync(name, options).ConfigureAwait(false); async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) @@ -254,6 +350,13 @@ namespace Discord.Rest else return null; } + async Task IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) + { + if (mode == CacheMode.AllowDownload) + return await GetOwnerAsync(options).ConfigureAwait(false); + else + return null; + } async Task> IGuild.GetUsersAsync(CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index b46107dc6..c1bec756c 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Audio; +using Discord.Audio; using Discord.Rest; using System; using System.Collections.Concurrent; @@ -44,15 +43,16 @@ namespace Discord.WebSocket public int MemberCount { get; set; } public int DownloadedMemberCount { get; private set; } - public ulong? AFKChannelId { get; private set; } - public ulong? EmbedChannelId { get; private set; } + internal ulong? AFKChannelId { get; private set; } + internal ulong? EmbedChannelId { get; private set; } public ulong OwnerId { get; private set; } + public SocketGuildUser Owner => GetUser(OwnerId); public string VoiceRegionId { get; private set; } public string IconId { get; private set; } public string SplashId { get; private set; } public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); - public ulong DefaultChannelId => Id; + public SocketTextChannel DefaultChannel => GetTextChannel(Id); public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); public bool HasAllMembers => _downloaderPromise.Task.IsCompleted; @@ -60,6 +60,26 @@ namespace Discord.WebSocket public Task SyncPromise => _syncPromise.Task; public Task DownloaderPromise => _downloaderPromise.Task; public IAudioClient AudioClient => _audioClient; + public SocketVoiceChannel AFKChannel + { + get + { + var id = AFKChannelId; + return id.HasValue ? GetVoiceChannel(id.Value) : null; + } + } + public SocketVoiceChannel EmbedChannel + { + get + { + var id = EmbedChannelId; + return id.HasValue ? GetVoiceChannel(id.Value) : null; + } + } + public IReadOnlyCollection TextChannels + => 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 SocketGuildUser CurrentUser { get @@ -286,6 +306,10 @@ namespace Discord.WebSocket return channel; return null; } + public SocketTextChannel GetTextChannel(ulong id) + => GetChannel(id) as SocketTextChannel; + public SocketVoiceChannel GetVoiceChannel(ulong id) + => GetChannel(id) as SocketVoiceChannel; public Task CreateTextChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) @@ -560,8 +584,11 @@ namespace Discord.WebSocket internal SocketGuild Clone() => MemberwiseClone() as SocketGuild; //IGuild - bool IGuild.Available => true; + ulong? IGuild.AFKChannelId => AFKChannelId; IAudioClient IGuild.AudioClient => null; + bool IGuild.Available => true; + ulong IGuild.DefaultChannelId => Id; + ulong? IGuild.EmbedChannelId => EmbedChannelId; IRole IGuild.EveryoneRole => EveryoneRole; IReadOnlyCollection IGuild.Roles => Roles; @@ -572,6 +599,20 @@ namespace Discord.WebSocket => Task.FromResult>(Channels); Task IGuild.GetChannelAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetChannel(id)); + Task> IGuild.GetTextChannelsAsync(CacheMode mode, RequestOptions options) + => Task.FromResult>(TextChannels); + Task IGuild.GetTextChannelAsync(ulong id, CacheMode mode, RequestOptions options) + => Task.FromResult(GetTextChannel(id)); + Task> IGuild.GetVoiceChannelsAsync(CacheMode mode, RequestOptions options) + => Task.FromResult>(VoiceChannels); + Task IGuild.GetVoiceChannelAsync(ulong id, CacheMode mode, RequestOptions options) + => Task.FromResult(GetVoiceChannel(id)); + Task IGuild.GetAFKChannelAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(AFKChannel); + Task IGuild.GetDefaultChannelAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(DefaultChannel); + Task IGuild.GetEmbedChannelAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(EmbedChannel); async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options) => await CreateTextChannelAsync(name, options).ConfigureAwait(false); async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) @@ -596,6 +637,8 @@ namespace Discord.WebSocket => Task.FromResult(GetUser(id)); Task IGuild.GetCurrentUserAsync(CacheMode mode, RequestOptions options) => Task.FromResult(CurrentUser); + Task IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(Owner); Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } } } From fe35400498b094ac8e738d1164d3872da13a99cb Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:42:28 -0400 Subject: [PATCH 164/263] Cleanup --- src/Discord.Net.Commands/Info/ModuleInfo.cs | 1 - src/Discord.Net.Commands/Info/ParameterInfo.cs | 1 - src/Discord.Net.Core/Audio/IAudioClient.cs | 1 - src/Discord.Net.Core/Extensions/GuildUserExtensions.cs | 1 - src/Discord.Net.Core/Utils/Preconditions.cs | 1 - src/Discord.Net.Rest/DiscordRestClient.cs | 3 +-- .../Entities/Channels/RestGuildChannel.cs | 3 +-- src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs | 1 - src/Discord.Net.Rest/Entities/Roles/RestRole.cs | 3 +-- src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs | 3 +-- src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs | 3 +-- src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs | 3 +-- src/Discord.Net.Rest/Net/Converters/ImageConverter.cs | 3 +-- src/Discord.Net.Rpc/DiscordRpcApiClient.cs | 2 +- .../Entities/Channels/RpcGuildChannel.cs | 1 - .../Entities/Channels/RpcTextChannel.cs | 3 +-- .../Entities/Messages/RpcUserMessage.cs | 3 +-- .../Entities/Users/UserVoiceProperties.cs | 10 ---------- src/Discord.Net.WebSocket/Audio/AudioClient.cs | 1 - .../Entities/Channels/SocketGuildChannel.cs | 3 +-- .../Entities/Channels/SocketTextChannel.cs | 3 +-- src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs | 3 +-- .../Entities/Users/SocketGuildUser.cs | 3 +-- .../Entities/Users/SocketSelfUser.cs | 3 +-- 24 files changed, 15 insertions(+), 47 deletions(-) delete mode 100644 src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 40a434592..198d2a921 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.Commands/Info/ParameterInfo.cs b/src/Discord.Net.Commands/Info/ParameterInfo.cs index 45ce3f0f4..a0cdf03d7 100644 --- a/src/Discord.Net.Commands/Info/ParameterInfo.cs +++ b/src/Discord.Net.Commands/Info/ParameterInfo.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading.Tasks; diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index dfc6f60bd..472ad32f1 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Threading.Tasks; namespace Discord.Audio diff --git a/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs b/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs index 855551c3d..9d152adf9 100644 --- a/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/GuildUserExtensions.cs @@ -6,7 +6,6 @@ namespace Discord { public static class GuildUserExtensions { - //TODO: Should we remove Add/Remove? Encourages race conditions. public static Task AddRolesAsync(this IGuildUser user, params IRole[] roles) => ChangeRolesAsync(user, add: roles); public static Task AddRolesAsync(this IGuildUser user, IEnumerable roles) diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 02cc5d288..14a730b7e 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; namespace Discord { diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index d00a7e1fe..384e43821 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -1,5 +1,4 @@ -using Discord.Net.Queue; -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Threading.Tasks; diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index c3b14cbe6..c11d76254 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; diff --git a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs index 8ec428178..80a49e34e 100644 --- a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs +++ b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Model = Discord.API.Invite; namespace Discord.Rest { diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs index e39dea1a1..7e9f83855 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.Role; diff --git a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs index d6b7c597f..0081351f0 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RoleHelper.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Threading.Tasks; using Model = Discord.API.Role; diff --git a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs index ee3551fd5..36bda64d5 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; diff --git a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs index 3afaff777..ab5ec4a3b 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestSelfUser.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; using Model = Discord.API.User; diff --git a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs b/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs index e82b7952b..f4d591d7e 100644 --- a/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/ImageConverter.cs @@ -1,5 +1,4 @@ -using Discord.API; -using Newtonsoft.Json; +using Newtonsoft.Json; using System; using Model = Discord.API.Image; diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs index 05e047067..b1ac7121a 100644 --- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -237,7 +237,7 @@ namespace Discord.API options = RequestOptions.CreateOrClone(options); var msg = new AuthenticateParams { - AccessToken = _authToken + AccessToken = AuthToken }; options.IgnoreState = true; return await SendRpcAsync("AUTHENTICATE", msg, options: options).ConfigureAwait(false); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 775798677..99fb3ecd5 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Discord.API.Rest; using Model = Discord.API.Rpc.Channel; using Discord.Rest; diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs index 29df5f643..9a88072b9 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcTextChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs index 71f35126a..240290fab 100644 --- a/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs +++ b/src/Discord.Net.Rpc/Entities/Messages/RpcUserMessage.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs b/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs deleted file mode 100644 index 68409ae95..000000000 --- a/src/Discord.Net.Rpc/Entities/Users/UserVoiceProperties.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Discord.Rpc.Entities.Users -{ - class UserVoiceProperties - { - } -} diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 784cdf194..5bedf1786 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -5,7 +5,6 @@ using Discord.WebSocket; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; -using System.IO; using System.Linq; using System.Text; using System.Threading; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 58c93ebaf..207a60a4f 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index ae2564959..98aefcf9b 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs index ce46aa24f..ad4b3afff 100644 --- a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs +++ b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Diagnostics; using System.Threading.Tasks; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 720f39c04..23e0768d5 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs index fbfdc4cbe..0f6d4e4f1 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketSelfUser.cs @@ -1,5 +1,4 @@ -using Discord.API.Rest; -using Discord.Rest; +using Discord.Rest; using System; using System.Diagnostics; using System.Threading.Tasks; From d9802593aba0325335ab06bafa212d559db838b5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 11:55:41 -0400 Subject: [PATCH 165/263] Started new testbed --- .../Discord.Net.Tests.csproj | 108 +--- test/Discord.Net.Tests/Net/CacheInfo.cs | 12 + .../Discord.Net.Tests/Net/CachedRestClient.cs | 120 +++++ .../Net/FilesystemProvider.cs | 124 +++++ test/Discord.Net.Tests/Net/HttpMixin.cs | 139 +++++ .../Properties/AssemblyInfo.cs | 36 -- test/Discord.Net.Tests/TestConfig.cs | 21 + test/Discord.Net.Tests/Tests.Channels.cs | 142 +++++ test/Discord.Net.Tests/Tests.Migrations.cs | 72 +++ test/Discord.Net.Tests/Tests.cs | 507 ++---------------- test/Discord.Net.Tests/config.json.example | 14 - test/Discord.Net.Tests/packages.config | 4 - 12 files changed, 682 insertions(+), 617 deletions(-) create mode 100644 test/Discord.Net.Tests/Net/CacheInfo.cs create mode 100644 test/Discord.Net.Tests/Net/CachedRestClient.cs create mode 100644 test/Discord.Net.Tests/Net/FilesystemProvider.cs create mode 100644 test/Discord.Net.Tests/Net/HttpMixin.cs delete mode 100644 test/Discord.Net.Tests/Properties/AssemblyInfo.cs create mode 100644 test/Discord.Net.Tests/TestConfig.cs create mode 100644 test/Discord.Net.Tests/Tests.Channels.cs create mode 100644 test/Discord.Net.Tests/Tests.Migrations.cs delete mode 100644 test/Discord.Net.Tests/config.json.example delete mode 100644 test/Discord.Net.Tests/packages.config diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 2a50610cc..2968fc10e 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -1,97 +1,27 @@ - - + - Debug - AnyCPU - {855D6B1D-847B-42DA-BE6A-23683EA89511} - Library - Properties - Discord.Tests - Discord.Net.Tests - v4.6.1 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - + Exe + netcoreapp1.1 + $(PackageTargetFallback);portable-net45+win8+wp8+wpa81 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll - True - - - - - - - - - - - - - - - - - + + + + PreserveNewest + - + + + - - {c6a50d24-cbd3-4e76-852c-4dca60bbd608} - Discord.Net.Net45 - + + + + + + - - - - - False - - - False - - - False - - - False - - - - - - - - \ No newline at end of file + diff --git a/test/Discord.Net.Tests/Net/CacheInfo.cs b/test/Discord.Net.Tests/Net/CacheInfo.cs new file mode 100644 index 000000000..ed2820b8e --- /dev/null +++ b/test/Discord.Net.Tests/Net/CacheInfo.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Discord.Net +{ + internal class CacheInfo + { + [JsonProperty("guild_id")] + public ulong? GuildId { get; set; } + [JsonProperty("version")] + public uint Version { get; set; } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Net/CachedRestClient.cs b/test/Discord.Net.Tests/Net/CachedRestClient.cs new file mode 100644 index 000000000..324510688 --- /dev/null +++ b/test/Discord.Net.Tests/Net/CachedRestClient.cs @@ -0,0 +1,120 @@ +using Akavache; +using Akavache.Sqlite3; +using Discord.Net.Rest; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Splat; +using System.Reactive.Concurrency; + +namespace Discord.Net +{ + internal class CachedRestClient : IRestClient + { + private readonly Dictionary _headers; + private IBlobCache _blobCache; + private string _baseUrl; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private bool _isDisposed; + + public CacheInfo Info { get; private set; } + + public CachedRestClient() + { + _headers = new Dictionary(); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationToken.None; + _parentToken = CancellationToken.None; + + Locator.CurrentMutable.Register(() => Scheduler.Default, typeof(IScheduler), "Taskpool"); + Locator.CurrentMutable.Register(() => new FilesystemProvider(), typeof(IFilesystemProvider), null); + Locator.CurrentMutable.Register(() => new HttpMixin(), typeof(IAkavacheHttpMixin), null); + //new Akavache.Sqlite3.Registrations().Register(Locator.CurrentMutable); + _blobCache = new SQLitePersistentBlobCache("cache.db"); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + _blobCache.Dispose(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + public void SetUrl(string url) + { + _baseUrl = url; + } + public void SetHeader(string key, string value) + { + _headers[key] = value; + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(string method, string endpoint, CancellationToken cancelToken, bool headerOnly) + { + if (method != "GET") + throw new InvalidOperationException("This RestClient only supports GET requests."); + + string uri = Path.Combine(_baseUrl, endpoint); + var bytes = await _blobCache.DownloadUrl(uri, _headers); + return new RestResponse(HttpStatusCode.OK, _headers, new MemoryStream(bytes)); + } + public Task SendAsync(string method, string endpoint, string json, CancellationToken cancelToken, bool headerOnly) + { + throw new InvalidOperationException("This RestClient does not support payloads."); + } + public Task SendAsync(string method, string endpoint, IReadOnlyDictionary multipartParams, CancellationToken cancelToken, bool headerOnly) + { + throw new InvalidOperationException("This RestClient does not support multipart requests."); + } + + public async Task ClearAsync() + { + await _blobCache.InvalidateAll(); + } + + public async Task LoadInfoAsync(ulong guildId) + { + if (Info != null) + return; + + bool needsReset = false; + try + { + Info = await _blobCache.GetObject("info"); + if (Info.GuildId != guildId) + needsReset = true; + } + catch (KeyNotFoundException) + { + needsReset = true; + } + if (needsReset) + { + Info = new CacheInfo() { GuildId = guildId, Version = 0 }; + await SaveInfoAsync().ConfigureAwait(false); + } + } + public async Task SaveInfoAsync() + { + await ClearAsync().ConfigureAwait(false); //Version changed, invalidate cache + await _blobCache.InsertObject("info", Info); + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Net/FilesystemProvider.cs b/test/Discord.Net.Tests/Net/FilesystemProvider.cs new file mode 100644 index 000000000..efa56ff66 --- /dev/null +++ b/test/Discord.Net.Tests/Net/FilesystemProvider.cs @@ -0,0 +1,124 @@ +using Akavache; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reactive; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reflection; + +namespace Discord +{ + public class FilesystemProvider : IFilesystemProvider + { + public IObservable OpenFileForReadAsync(string path, IScheduler scheduler) + { + return SafeOpenFileAsync(path, FileMode.Open, FileAccess.Read, FileShare.Read, scheduler); + } + + public IObservable OpenFileForWriteAsync(string path, IScheduler scheduler) + { + return SafeOpenFileAsync(path, FileMode.Create, FileAccess.Write, FileShare.None, scheduler); + } + + public IObservable CreateRecursive(string path) + { + CreateRecursive(new DirectoryInfo(path)); + return Observable.Return(Unit.Default); + } + + public IObservable Delete(string path) + { + return Observable.Start(() => File.Delete(path), Scheduler.Default); + } + + public string GetDefaultRoamingCacheDirectory() + { + throw new NotSupportedException(); + } + + public string GetDefaultSecretCacheDirectory() + { + throw new NotSupportedException(); + } + + public string GetDefaultLocalMachineCacheDirectory() + { + throw new NotSupportedException(); + } + + protected static string GetAssemblyDirectoryName() + { + var assemblyDirectoryName = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); + Debug.Assert(assemblyDirectoryName != null, "The directory name of the assembly location is null"); + return assemblyDirectoryName; + } + + private static IObservable SafeOpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share, IScheduler scheduler = null) + { + scheduler = scheduler ?? Scheduler.Default; + var ret = new AsyncSubject(); + + Observable.Start(() => + { + try + { + var createModes = new[] + { + FileMode.Create, + FileMode.CreateNew, + FileMode.OpenOrCreate, + }; + + + // NB: We do this (even though it's incorrect!) because + // throwing lots of 1st chance exceptions makes debugging + // obnoxious, as well as a bug in VS where it detects + // exceptions caught by Observable.Start as Unhandled. + if (!createModes.Contains(mode) && !File.Exists(path)) + { + ret.OnError(new FileNotFoundException()); + return; + } + + Observable.Start(() => new FileStream(path, mode, access, share, 4096, false), scheduler).Cast().Subscribe(ret); + } + catch (Exception ex) + { + ret.OnError(ex); + } + }, scheduler); + + return ret; + } + private static void CreateRecursive(DirectoryInfo info) + { + SplitFullPath(info).Aggregate((parent, dir) => + { + var path = Path.Combine(parent, dir); + if (!Directory.Exists(path)) + Directory.CreateDirectory(path); + return path; + }); + } + + private static IEnumerable SplitFullPath(DirectoryInfo info) + { + var root = Path.GetPathRoot(info.FullName); + var components = new List(); + for (var path = info.FullName; path != root && path != null; path = Path.GetDirectoryName(path)) + { + var filename = Path.GetFileName(path); + if (String.IsNullOrEmpty(filename)) + continue; + components.Add(filename); + } + components.Add(root); + components.Reverse(); + return components; + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Net/HttpMixin.cs b/test/Discord.Net.Tests/Net/HttpMixin.cs new file mode 100644 index 000000000..0c7bf2c3d --- /dev/null +++ b/test/Discord.Net.Tests/Net/HttpMixin.cs @@ -0,0 +1,139 @@ +using Akavache; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Text; +using System.Reactive; +using System.Reactive.Threading.Tasks; + +namespace Discord.Net +{ + public class HttpMixin : IAkavacheHttpMixin + { + /// + /// Download data from an HTTP URL and insert the result into the + /// cache. If the data is already in the cache, this returns + /// a cached value. The URL itself is used as the key. + /// + /// The URL to download. + /// An optional Dictionary containing the HTTP + /// request headers. + /// Force a web request to always be issued, skipping the cache. + /// An optional expiration date. + /// The data downloaded from the URL. + public IObservable DownloadUrl(IBlobCache This, string url, IDictionary headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) + { + return This.DownloadUrl(url, url, headers, fetchAlways, absoluteExpiration); + } + + /// + /// Download data from an HTTP URL and insert the result into the + /// cache. If the data is already in the cache, this returns + /// a cached value. An explicit key is provided rather than the URL itself. + /// + /// The key to store with. + /// The URL to download. + /// An optional Dictionary containing the HTTP + /// request headers. + /// Force a web request to always be issued, skipping the cache. + /// An optional expiration date. + /// The data downloaded from the URL. + public IObservable DownloadUrl(IBlobCache This, string key, string url, IDictionary headers = null, bool fetchAlways = false, DateTimeOffset? absoluteExpiration = null) + { + var doFetch = MakeWebRequest(new Uri(url), headers).SelectMany(x => ProcessWebResponse(x, url, absoluteExpiration)); + var fetchAndCache = doFetch.SelectMany(x => This.Insert(key, x, absoluteExpiration).Select(_ => x)); + + var ret = default(IObservable); + if (!fetchAlways) + { + ret = This.Get(key).Catch(fetchAndCache); + } + else + { + ret = fetchAndCache; + } + + var conn = ret.PublishLast(); + conn.Connect(); + return conn; + } + + IObservable ProcessWebResponse(WebResponse wr, string url, DateTimeOffset? absoluteExpiration) + { + var hwr = (HttpWebResponse)wr; + Debug.Assert(hwr != null, "The Web Response is somehow null but shouldn't be."); + if ((int)hwr.StatusCode >= 400) + { + return Observable.Throw(new WebException(hwr.StatusDescription)); + } + + var ms = new MemoryStream(); + using (var responseStream = hwr.GetResponseStream()) + { + Debug.Assert(responseStream != null, "The response stream is somehow null"); + responseStream.CopyTo(ms); + } + + var ret = ms.ToArray(); + return Observable.Return(ret); + } + + static IObservable MakeWebRequest( + Uri uri, + IDictionary headers = null, + string content = null, + int retries = 3, + TimeSpan? timeout = null) + { + IObservable request; + + request = Observable.Defer(() => + { + var hwr = CreateWebRequest(uri, headers); + + if (content == null) + return Observable.FromAsyncPattern(hwr.BeginGetResponse, hwr.EndGetResponse)(); + + var buf = Encoding.UTF8.GetBytes(content); + + // NB: You'd think that BeginGetResponse would never block, + // seeing as how it's asynchronous. You'd be wrong :-/ + var ret = new AsyncSubject(); + Observable.Start(() => + { + Observable.FromAsyncPattern(hwr.BeginGetRequestStream, hwr.EndGetRequestStream)() + .SelectMany(x => WriteAsyncRx(x, buf, 0, buf.Length)) + .SelectMany(_ => Observable.FromAsyncPattern(hwr.BeginGetResponse, hwr.EndGetResponse)()) + .Multicast(ret).Connect(); + }, BlobCache.TaskpoolScheduler); + + return ret; + }); + + return request.Timeout(timeout ?? TimeSpan.FromSeconds(15), BlobCache.TaskpoolScheduler).Retry(retries); + } + + private static WebRequest CreateWebRequest(Uri uri, IDictionary headers) + { + var hwr = WebRequest.Create(uri); + if (headers != null) + { + foreach (var x in headers) + { + hwr.Headers[x.Key] = x.Value; + } + } + return hwr; + } + + private static IObservable WriteAsyncRx(Stream stream, byte[] data, int start, int length) + { + return stream.WriteAsync(data, start, length).ToObservable(); + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Properties/AssemblyInfo.cs b/test/Discord.Net.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5b1c7b125..000000000 --- a/test/Discord.Net.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Discord.Net.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Discord.Net.Tests")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("855d6b1d-847b-42da-be6a-23683ea89511")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/Discord.Net.Tests/TestConfig.cs b/test/Discord.Net.Tests/TestConfig.cs new file mode 100644 index 000000000..21c2dcb8d --- /dev/null +++ b/test/Discord.Net.Tests/TestConfig.cs @@ -0,0 +1,21 @@ +using Newtonsoft.Json; +using System.IO; + +namespace Discord +{ + internal class TestConfig + { + [JsonProperty("token")] + public string Token { get; private set; } + [JsonProperty("guild_id")] + public ulong GuildId { get; private set; } + + public static TestConfig LoadFile(string path) + { + using (var stream = new FileStream(path, FileMode.Open)) + using (var reader = new StreamReader(stream)) + using (var jsonReader = new JsonTextReader(reader)) + return new JsonSerializer().Deserialize(jsonReader); + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs new file mode 100644 index 000000000..1e707b329 --- /dev/null +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -0,0 +1,142 @@ +using Discord.Rest; +using System.Linq; +using System.Threading.Tasks; +using Xunit; + +namespace Discord +{ + public partial class Tests + { + internal static async Task Migration_CreateTextChannels(DiscordRestClient client, RestGuild guild) + { + var text1 = await guild.GetDefaultChannelAsync(); + var text2 = await guild.CreateTextChannelAsync("text2"); + var text3 = await guild.CreateTextChannelAsync("text3"); + var text4 = await guild.CreateTextChannelAsync("text4"); + var text5 = await guild.CreateTextChannelAsync("text5"); + + //Modify #general + await text1.ModifyAsync(x => + { + x.Name = "text1"; + x.Position = 1; + x.Topic = "Topic1"; + }); + + await text2.ModifyAsync(x => + { + x.Position = 2; + }); + await text3.ModifyAsync(x => + { + x.Topic = "Topic2"; + }); + await text4.ModifyAsync(x => + { + x.Position = 3; + x.Topic = "Topic2"; + }); + await text5.ModifyAsync(x => + { + }); + + CheckTextChannels(guild, text1, text2, text3, text4, text5); + } + [Fact] + public async Task TestTextChannels() + { + CheckTextChannels(_guild, (await _guild.GetTextChannelsAsync()).ToArray()); + } + private static void CheckTextChannels(RestGuild guild, params RestTextChannel[] textChannels) + { + Assert.Equal(textChannels.Length, 5); + Assert.All(textChannels, x => + { + Assert.NotNull(x); + Assert.NotEqual(x.Id, 0UL); + Assert.True(x.Position >= 0); + }); + + var text1 = textChannels.Where(x => x.Name == "text1").FirstOrDefault(); + var text2 = textChannels.Where(x => x.Name == "text2").FirstOrDefault(); + var text3 = textChannels.Where(x => x.Name == "text3").FirstOrDefault(); + var text4 = textChannels.Where(x => x.Name == "text4").FirstOrDefault(); + var text5 = textChannels.Where(x => x.Name == "text5").FirstOrDefault(); + + Assert.NotNull(text1); + Assert.True(text1.Id == guild.DefaultChannelId); + Assert.Equal(text1.Position, 1); + Assert.Equal(text1.Topic, "Topic1"); + + Assert.NotNull(text2); + Assert.Equal(text2.Position, 2); + Assert.Null(text2.Topic); + + Assert.NotNull(text3); + Assert.Equal(text3.Topic, "Topic2"); + + Assert.NotNull(text4); + Assert.Equal(text4.Position, 3); + Assert.Equal(text4.Topic, "Topic2"); + + Assert.NotNull(text5); + Assert.Null(text5.Topic); + } + + internal static async Task Migration_CreateVoiceChannels(DiscordRestClient client, RestGuild guild) + { + var voice1 = await guild.CreateVoiceChannelAsync("voice1"); + var voice2 = await guild.CreateVoiceChannelAsync("voice2"); + var voice3 = await guild.CreateVoiceChannelAsync("voice3"); + + await voice1.ModifyAsync(x => + { + x.Bitrate = 96000; + x.Position = 1; + x.UserLimit = 8; + }); + await voice2.ModifyAsync(x => + { + x.Bitrate = 64000; + x.Position = 1; + x.UserLimit = null; + }); + await voice3.ModifyAsync(x => + { + x.Bitrate = 8000; + x.Position = 1; + x.UserLimit = 16; + }); + + CheckVoiceChannels(guild, voice1, voice2, voice3); + } + [Fact] + public async Task TestVoiceChannels() + { + CheckVoiceChannels(_guild, (await _guild.GetVoiceChannelsAsync()).ToArray()); + } + private static void CheckVoiceChannels(RestGuild guild, params RestVoiceChannel[] voiceChannels) + { + Assert.Equal(voiceChannels.Length, 3); + + var voice1 = voiceChannels.Where(x => x.Name == "voice1").FirstOrDefault(); + var voice2 = voiceChannels.Where(x => x.Name == "voice2").FirstOrDefault(); + var voice3 = voiceChannels.Where(x => x.Name == "voice3").FirstOrDefault(); + + Assert.NotNull(voice1); + Assert.Equal(voice1.Bitrate, 96000); + Assert.Equal(voice1.Position, 1); + Assert.Equal(voice1.UserLimit, 8); + + Assert.NotNull(voice2); + Assert.Equal(voice2.Bitrate, 64000); + Assert.Equal(voice2.Position, 1); + Assert.Equal(voice2.UserLimit, null); + + Assert.NotNull(voice3); + Assert.Equal(voice3.Bitrate, 8000); + Assert.Equal(voice3.Position, 1); + Assert.Equal(voice3.UserLimit, 16); + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Tests.Migrations.cs b/test/Discord.Net.Tests/Tests.Migrations.cs new file mode 100644 index 000000000..e786329cd --- /dev/null +++ b/test/Discord.Net.Tests/Tests.Migrations.cs @@ -0,0 +1,72 @@ +using System; +using System.Threading.Tasks; +using Discord.Rest; + +namespace Discord +{ + public partial class TestsFixture + { + public const uint MigrationCount = 3; + + public async Task MigrateAsync() + { + DiscordRestClient client = null; + RestGuild guild = null; + + await _cache.LoadInfoAsync(_config.GuildId).ConfigureAwait(false); + while (_cache.Info.Version != MigrationCount) + { + if (client == null) + { + client = new DiscordRestClient(); + await client.LoginAsync(TokenType.Bot, _config.Token, false).ConfigureAwait(false); + guild = await client.GetGuildAsync(_config.GuildId); + } + + uint nextVer = _cache.Info.Version + 1; + try + { + await DoMigrateAsync(client, guild, nextVer).ConfigureAwait(false); + _cache.Info.Version = nextVer; + await _cache.SaveInfoAsync().ConfigureAwait(false); + } + catch + { + await _cache.ClearAsync().ConfigureAwait(false); + throw; + } + } + } + + private static Task DoMigrateAsync(DiscordRestClient client, RestGuild guild, uint toVersion) + { + switch (toVersion) + { + case 1: return Migration_WipeGuild(client, guild); + case 2: return Tests.Migration_CreateTextChannels(client, guild); + case 3: return Tests.Migration_CreateVoiceChannels(client, guild); + default: throw new InvalidOperationException("Unknown migration: " + toVersion); + } + } + + private static async Task Migration_WipeGuild(DiscordRestClient client, RestGuild guild) + { + var textChannels = await guild.GetTextChannelsAsync(); + var voiceChannels = await guild.GetVoiceChannelsAsync(); + var roles = guild.Roles; + + foreach (var channel in textChannels) + { + if (channel.Id != guild.DefaultChannelId) + await channel.DeleteAsync(); + } + foreach (var channel in voiceChannels) + await channel.DeleteAsync(); + foreach (var role in roles) + { + if (role.Id != guild.EveryoneRole.Id) + await role.DeleteAsync(); + } + } + } +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/Tests.cs b/test/Discord.Net.Tests/Tests.cs index d8d09cd3d..df156d254 100644 --- a/test/Discord.Net.Tests/Tests.cs +++ b/test/Discord.Net.Tests/Tests.cs @@ -1,494 +1,53 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; +using Discord.Net; +using Discord.Rest; +using Xunit; -namespace Discord.Tests +namespace Discord { - //TODO: Tests are massively incomplete and out of date, needing a full rewrite - - [TestClass] - public class Tests + public partial class TestsFixture : IDisposable { - private const int EventTimeout = 10000; //Max time in milliseconds to wait for an event response from our test actions - - private static DiscordSocketClient _hostBot, _targetBot, _observerBot; - private static Guild _testGuild; - private static TextChannel _testGuildChannel; - private static Random _random; - private static PublicInvite _testGuildInvite; - - private static TestContext _context; - - private static string _hostToken; - private static string _observerToken; - private static string _targetToken; - - private static string GetRandomText() - { - lock (_random) - return $"test_{_random.Next()}"; - } - - #region Initialization - - [ClassInitialize] - public static void Initialize(TestContext testContext) - { - _context = testContext; - - _hostToken = Environment.GetEnvironmentVariable("discord-unit-host_token"); - _observerToken = Environment.GetEnvironmentVariable("discord-unit-observer_token"); - _targetToken = Environment.GetEnvironmentVariable("discord-unit-target_token"); - } - - [TestMethod] - [Priority(1)] - public async Task TestInitialize() - { - _context.WriteLine("Initializing."); - - _random = new Random(); - - _hostBot = new DiscordSocketClient(_hostToken); - _targetBot = new DiscordSocketClient(_targetToken); - _observerBot = new DiscordSocketClient(_observerToken); - - await _hostBot.Login(); - - await Task.Delay(3000); - - //Cleanup existing Guilds - (await _hostBot.GetGuilds()).Select(x => x.Owner.Id == _hostBot.CurrentUser.Id ? x.Delete() : x.Leave()); - - //Create new Guild and invite the other bots to it - - _testGuild = await _hostBot.CreateGuild("Discord.Net Testing", _hostBot.GetOptimalVoiceRegion()); - - await Task.Delay(1000); - - PublicInvite invite = await _testGuild.CreateInvite(60, 3, false, false); - _testGuildInvite = invite; - - _context.WriteLine($"Host: {_hostBot.CurrentUser.Username} in {(await _hostBot.GetGuilds()).Count()}"); - } - - [TestMethod] - [Priority(2)] - public async Task TestTokenLogin_Ready() - { - AssertEvent( - "READY never received", - async () => await _observerBot.Login(), - x => _observerBot.Connected += x, - x => _observerBot.Connected -= x, - null, - true); - (await _observerBot.GetGuilds()).Select(x => x.Owner.Id == _observerBot.CurrentUser.Id ? x.Delete() : x.Leave()); - await _observerBot.RestClient.Send(new API.Rest.AcceptInviteRequest(_testGuildInvite.Code)); - } - - [TestMethod] - [Priority(2)] - public async Task TestReady() - { - AssertEvent( - "READY never received", - async () => await _targetBot.Login(), - x => _targetBot.Connected += x, - x => _targetBot.Connected -= x, - null, - true); - - (await _targetBot.GetGuilds()).Select(x => x.Owner.Id == _targetBot.CurrentUser.Id ? x.Delete() : x.Leave()); - _testGuildChannel = _testGuild.DefaultChannel; - } - - #endregion + private readonly TestConfig _config; + private readonly CachedRestClient _cache; + internal readonly DiscordRestClient _client; + internal readonly RestGuild _guild; - // Guilds - - #region Guild Tests - - [TestMethod] - [Priority(3)] - public void TestJoinedGuild() + public TestsFixture() { - AssertEvent( - "Never Got JoinedGuild", - async () => await _targetBot.RestClient.Send(new API.Rest.AcceptInviteRequest(_testGuildInvite.Code)), - x => _targetBot.JoinedGuild += x, - x => _targetBot.JoinedGuild -= x); - } - - #endregion - - #region Channel Tests + _cache = new CachedRestClient(); - //Channels - [TestMethod] - public void TestCreateTextChannel() - { - GuildChannel channel = null; - string name = GetRandomText(); - AssertEvent( - "ChannelCreated event never received", - async () => channel = await _testGuild.CreateTextChannel(name), - x => _targetBot.ChannelCreated += x, - x => _targetBot.ChannelCreated -= x, - (s, e) => e.Channel.Id == channel.Id); - - AssertEvent( - "ChannelDestroyed event never received", - async () => await channel.Delete(), - x => _targetBot.ChannelDestroyed += x, - x => _targetBot.ChannelDestroyed -= x, - (s, e) => e.Channel.Id == channel.Id); - } - [TestMethod] - public void TestCreateVoiceChannel() - { - GuildChannel channel = null; - string name = GetRandomText(); - AssertEvent( - "ChannelCreated event never received", - async () => channel = await _testGuild.CreateVoiceChannel(name), - x => _targetBot.ChannelCreated += x, - x => _targetBot.ChannelCreated -= x, - (s, e) => e.Channel.Id == channel.Id); - - AssertEvent( - "ChannelDestroyed event never received", - async () => await channel.Delete(), - x => _targetBot.ChannelDestroyed += x, - x => _targetBot.ChannelDestroyed -= x, - (s, e) => e.Channel.Id == channel.Id); - } - - [TestMethod] - [ExpectedException(typeof(Net.HttpException))] - public async Task TestCreateChannel_NoName() - { - await _testGuild.CreateTextChannel($""); - } - [TestMethod] - public async Task Test_CreateGetChannel() - { - var name = GetRandomText(); - var channel = await _testGuild.CreateTextChannel(name); - var get_channel = _testGuild.GetChannel(channel.Id); - Assert.AreEqual(channel.Id, get_channel.Id, "ID of Channel and GetChannel were not equal."); - } - [TestMethod] - public void TestSendTyping() - { - var channel = _testGuildChannel; - AssertEvent( - "UserUpdated event never fired.", - async () => await channel.TriggerTyping(), - x => _targetBot.UserIsTyping += x, - x => _targetBot.UserIsTyping -= x); - } - [TestMethod] - public void TestEditChannel() - { - var channel = _testGuildChannel; - AssertEvent( - "ChannelUpdated Never Received", - async () => await channel.Modify(x => - { - x.Name = GetRandomText(); - x.Topic = $"topic - {GetRandomText()}"; - x.Position = 26; - }), - x => _targetBot.ChannelUpdated += x, - x => _targetBot.ChannelUpdated -= x); - } - [TestMethod] - public void TestChannelMention() - { - var channel = _testGuildChannel; - Assert.AreEqual($"<#{channel.Id}>", channel.Mention, "Generated channel mention was not the expected channel mention."); - } - [TestMethod] - public void TestChannelUserCount() - { - Assert.AreEqual(3, _testGuildChannel.Users.Count(), "Read an incorrect number of users in a channel"); - } - - #endregion - - #region Message Tests - - //Messages - [TestMethod] - public async Task TestMessageEvents() - { - string name = GetRandomText(); - var channel = await _testGuild.CreateTextChannel(name); - _context.WriteLine($"Channel Name: {channel.Name} / {channel.Guild.Name}"); - string text = GetRandomText(); - Message message = null; - AssertEvent( - "MessageCreated event never received", - async () => message = await channel.SendMessage(text), - x => _targetBot.MessageReceived += x, - x => _targetBot.MessageReceived -= x, - (s, e) => e.Message.Text == text); - - AssertEvent( - "MessageUpdated event never received", - async () => await message.Modify(x => - { - x.Content = text + " updated"; - }), - x => _targetBot.MessageUpdated += x, - x => _targetBot.MessageUpdated -= x, - (s, e) => e.Before.Text == text && e.After.Text == text + " updated"); - - AssertEvent( - "MessageDeleted event never received", - async () => await message.Delete(), - x => _targetBot.MessageDeleted += x, - x => _targetBot.MessageDeleted -= x, - (s, e) => e.Message.Id == message.Id); - } - [TestMethod] - public async Task TestDownloadMessages() - { - string name = GetRandomText(); - var channel = await _testGuild.CreateTextChannel(name); - for (var i = 0; i < 10; i++) await channel.SendMessage(GetRandomText()); - while (channel.Discord.MessageQueue.Count > 0) await Task.Delay(100); - var messages = await channel.GetMessages(10); - Assert.AreEqual(10, messages.Count(), "Expected 10 messages in downloaded array, did not see 10."); - } - [TestMethod] - public async Task TestSendTTSMessage() - { - var channel = await _testGuild.CreateTextChannel(GetRandomText()); - AssertEvent( - "MessageCreated event never fired", - async () => await channel.SendMessage(GetRandomText(), true), - x => _targetBot.MessageReceived += x, - x => _targetBot.MessageReceived -= x, - (s, e) => e.Message.IsTTS); - } - - #endregion - - #region User Tests - - [TestMethod] - public async Task TestUserMentions() - { - var user = (await _targetBot.GetGuild(_testGuild.Id)).CurrentUser; - Assert.AreEqual($"<@{user.Id}>", user.Mention); - } - [TestMethod] - public void TestUserEdit() - { - var user = _testGuild.GetUser(_targetBot.CurrentUser.Id); - AssertEvent( - "UserUpdated never fired", - async () => await user.Modify(x => - { - x.Deaf = true; - x.Mute = true; - }), - x => _targetBot.UserUpdated += x, - x => _targetBot.UserUpdated -= x); - } - [TestMethod] - public void TestEditSelf() - { - throw new NotImplementedException(); - /*var name = RandomText - AssertEvent( - "UserUpdated never fired", - async () => await _targetBot.CurrentUser.Modify(TargetPassword, name), - x => _obGuildBot.UserUpdated += x, - x => _obGuildBot.UserUpdated -= x, - (s, e) => e.After.Username == name);*/ - } - [TestMethod] - public void TestSetStatus() - { - AssertEvent( - "UserUpdated never fired", - async () => await SetStatus(_targetBot, UserStatus.Idle), - x => _observerBot.UserUpdated += x, - x => _observerBot.UserUpdated -= x, - (s, e) => e.After.Status == UserStatus.Idle); - } - private Task SetStatus(DiscordClient _client, UserStatus status) - { - throw new NotImplementedException(); - /*_client.SetStatus(status); - await Task.Delay(50);*/ - } - [TestMethod] - public void TestSetGame() - { - AssertEvent( - "UserUpdated never fired", - async () => await SetGame(_targetBot, "test game"), - x => _observerBot.UserUpdated += x, - x => _observerBot.UserUpdated -= x, - (s, e) => _targetBot.CurrentUser.CurrentGame == "test game"); - - } - private Task SetGame(DiscordClient _client, string game) - { - throw new NotImplementedException(); - //_client.SetGame(game); - //await Task.Delay(5); - } - - #endregion - - #region Permission Tests - - // Permissions - [TestMethod] - public async Task Test_AddGet_PermissionsRule() - { - var channel = await _testGuild.CreateTextChannel(GetRandomText()); - var user = _testGuild.GetUser(_targetBot.CurrentUser.Id); - var perms = new OverwritePermissions(sendMessages: PermValue.Deny); - await channel.UpdatePermissionOverwrite(user, perms); - var resultPerms = channel.GetPermissionOverwrite(user); - Assert.IsNotNull(resultPerms, "Perms retrieved from Guild were null."); - } - [TestMethod] - public async Task Test_AddRemove_PermissionsRule() - { - var channel = await _testGuild.CreateTextChannel(GetRandomText()); - var user = _testGuild.GetUser(_targetBot.CurrentUser.Id); - var perms = new OverwritePermissions(sendMessages: PermValue.Deny); - await channel.UpdatePermissionOverwrite(user, perms); - await channel.RemovePermissionOverwrite(user); - await Task.Delay(200); - Assert.AreEqual(PermValue.Inherit, channel.GetPermissionOverwrite(user)?.SendMessages); - } - [TestMethod] - public async Task Test_Permissions_Event() - { - var channel = await _testGuild.CreateTextChannel(GetRandomText()); - var user = _testGuild.GetUser(_targetBot.CurrentUser.Id); - var perms = new OverwritePermissions(sendMessages: PermValue.Deny); - AssertEvent - ("ChannelUpdatedEvent never fired.", - async () => await channel.UpdatePermissionOverwrite(user, perms), - x => _targetBot.ChannelUpdated += x, - x => _targetBot.ChannelUpdated -= x, - (s, e) => e.Channel == channel && (e.After as GuildChannel).PermissionOverwrites.Count() != (e.Before as GuildChannel).PermissionOverwrites.Count()); - } - [TestMethod] - [ExpectedException(typeof(Net.HttpException))] - public async Task Test_Affect_Permissions_Invalid_Channel() - { - var channel = await _testGuild.CreateTextChannel(GetRandomText()); - var user = _testGuild.GetUser(_targetBot.CurrentUser.Id); - var perms = new OverwritePermissions(sendMessages: PermValue.Deny); - await channel.Delete(); - await channel.UpdatePermissionOverwrite(user, perms); - } - - #endregion - - - [ClassCleanup] - public static async Task Cleanup() - { - WaitMany( - (await _hostBot.GetGuilds()).Select(x => x.Owner.Id == _hostBot.CurrentUser.Id ? x.Delete() : x.Leave()), - (await _targetBot.GetGuilds()).Select(x => x.Owner.Id == _targetBot.CurrentUser.Id ? x.Delete() : x.Leave()), - (await _observerBot.GetGuilds()).Select(x => x.Owner.Id == _observerBot.CurrentUser.Id ? x.Delete() : x.Leave())); - - WaitAll( - _hostBot.Disconnect(), - _targetBot.Disconnect(), - _observerBot.Disconnect()); - } - - #region Helpers - - // Task Helpers - - private static void AssertEvent(string msg, Func action, Action> addEvent, Action> removeEvent, Func test = null) - { - AssertEvent(msg, action, addEvent, removeEvent, test, true); - } - private static void AssertNoEvent(string msg, Func action, Action> addEvent, Action> removeEvent, Func test = null) - { - AssertEvent(msg, action, addEvent, removeEvent, test, false); - } - private static void AssertEvent(string msg, Func action, Action> addEvent, Action> removeEvent, Func test, bool assertTrue) - { - ManualResetEventSlim trigger = new ManualResetEventSlim(false); - bool result = false; - - EventHandler handler = (s, e) => + _config = TestConfig.LoadFile("./config.json"); + var config = new DiscordRestConfig { - if (test != null) + RestClientProvider = url => { - result |= test(s, e); - trigger.Set(); + _cache.SetUrl(url); + return _cache; } - else - result = true; }; + _client = new DiscordRestClient(config); + _client.LoginAsync(TokenType.Bot, _config.Token).Wait(); - addEvent(handler); - var task = action(); - trigger.Wait(EventTimeout); - task.Wait(); - removeEvent(handler); - - Assert.AreEqual(assertTrue, result, msg); + MigrateAsync().Wait(); + _guild = _client.GetGuildAsync(_config.GuildId).Result; } - private static void AssertEvent(string msg, Func action, Action addEvent, Action removeEvent, Func test, bool assertTrue) + public void Dispose() { - ManualResetEventSlim trigger = new ManualResetEventSlim(false); - bool result = false; - - EventHandler handler = (s, e) => - { - if (test != null) - { - result |= test(s); - trigger.Set(); - } - else - result = true; - }; - - addEvent(handler); - var task = action(); - trigger.Wait(EventTimeout); - task.Wait(); - removeEvent(handler); - - Assert.AreEqual(assertTrue, result, msg); + _client.Dispose(); + _cache.Dispose(); } + } - private static void WaitAll(params Task[] tasks) - { - Task.WaitAll(tasks); - } - private static void WaitAll(IEnumerable tasks) - { - Task.WaitAll(tasks.ToArray()); - } - private static void WaitMany(params IEnumerable[] tasks) + public partial class Tests : IClassFixture + { + private DiscordRestClient _client; + private RestGuild _guild; + + public Tests(TestsFixture fixture) { - Task.WaitAll(tasks.Where(x => x != null).SelectMany(x => x).ToArray()); + _client = fixture._client; + _guild = fixture._guild; } - - #endregion } -} +} \ No newline at end of file diff --git a/test/Discord.Net.Tests/config.json.example b/test/Discord.Net.Tests/config.json.example deleted file mode 100644 index 638d65b4d..000000000 --- a/test/Discord.Net.Tests/config.json.example +++ /dev/null @@ -1,14 +0,0 @@ -{ - "user1": { - "email": "user1@example.com", - "password": "password123" - }, - "user2": { - "email": "user2@example.com", - "password": "password456" - }, - "user3": { - "email": "user3@example.com", - "password": "password789" - } -} \ No newline at end of file diff --git a/test/Discord.Net.Tests/packages.config b/test/Discord.Net.Tests/packages.config deleted file mode 100644 index 2abc396bb..000000000 --- a/test/Discord.Net.Tests/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file From bf8a615eef59fed9d61fb113199294a52e43f34e Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 13:46:32 -0400 Subject: [PATCH 166/263] Added config example --- test/Discord.Net.Tests/config.json.example | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 test/Discord.Net.Tests/config.json.example diff --git a/test/Discord.Net.Tests/config.json.example b/test/Discord.Net.Tests/config.json.example new file mode 100644 index 000000000..403afa3bd --- /dev/null +++ b/test/Discord.Net.Tests/config.json.example @@ -0,0 +1,4 @@ +{ + "token": "AAA.BBB.CCC", + "guild_id": 1234567890 +} \ No newline at end of file From 9fb413952fdef34f7e3674470e642efe5536037f Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 13:47:56 -0400 Subject: [PATCH 167/263] Fixed solution errors --- Discord.Net.sln | 4 +--- .../Discord.Net.Providers.UdpClient.csproj | 0 .../UDPClient.cs | 0 .../UDPClientProvider.cs | 0 4 files changed, 1 insertion(+), 3 deletions(-) rename src/{Discord.Net.Providers.UDPClient => Discord.Net.Providers.UdpClient}/Discord.Net.Providers.UdpClient.csproj (100%) rename src/{Discord.Net.Providers.UDPClient => Discord.Net.Providers.UdpClient}/UDPClient.cs (100%) rename src/{Discord.Net.Providers.UDPClient => Discord.Net.Providers.UdpClient}/UDPClientProvider.cs (100%) diff --git a/Discord.Net.sln b/Discord.Net.sln index c0715c29b..fbe1c9262 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -21,9 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.WS4Net", "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj", "{6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.API", "src\Discord.Net.API\Discord.Net.API.csproj", "{547261FC-8BA3-40EA-A040-A38ABDAA8D72}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.UDPClient", "src\Discord.Net.Providers.UDPClient\Discord.Net.Providers.UDPClient.csproj", "{ABC9F4B9-2452-4725-B522-754E0A02E282}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.UdpClient", "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj", "{ABC9F4B9-2452-4725-B522-754E0A02E282}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}" EndProject diff --git a/src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj similarity index 100% rename from src/Discord.Net.Providers.UDPClient/Discord.Net.Providers.UdpClient.csproj rename to src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj diff --git a/src/Discord.Net.Providers.UDPClient/UDPClient.cs b/src/Discord.Net.Providers.UdpClient/UDPClient.cs similarity index 100% rename from src/Discord.Net.Providers.UDPClient/UDPClient.cs rename to src/Discord.Net.Providers.UdpClient/UDPClient.cs diff --git a/src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs b/src/Discord.Net.Providers.UdpClient/UDPClientProvider.cs similarity index 100% rename from src/Discord.Net.Providers.UDPClient/UDPClientProvider.cs rename to src/Discord.Net.Providers.UdpClient/UDPClientProvider.cs From 0947bedb28bcdde88486277977f6b4dcd3fe2036 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 13:56:35 -0400 Subject: [PATCH 168/263] Cleaned up VoiceChannels test --- test/Discord.Net.Tests/Tests.Channels.cs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs index 1e707b329..d81d28f3e 100644 --- a/test/Discord.Net.Tests/Tests.Channels.cs +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -93,12 +93,9 @@ namespace Discord { x.Bitrate = 96000; x.Position = 1; - x.UserLimit = 8; }); await voice2.ModifyAsync(x => { - x.Bitrate = 64000; - x.Position = 1; x.UserLimit = null; }); await voice3.ModifyAsync(x => @@ -108,16 +105,24 @@ namespace Discord x.UserLimit = 16; }); - CheckVoiceChannels(guild, voice1, voice2, voice3); + CheckVoiceChannels(voice1, voice2, voice3); } [Fact] public async Task TestVoiceChannels() { - CheckVoiceChannels(_guild, (await _guild.GetVoiceChannelsAsync()).ToArray()); + CheckVoiceChannels((await _guild.GetVoiceChannelsAsync()).ToArray()); } - private static void CheckVoiceChannels(RestGuild guild, params RestVoiceChannel[] voiceChannels) + private static void CheckVoiceChannels(params RestVoiceChannel[] voiceChannels) { Assert.Equal(voiceChannels.Length, 3); + Assert.All(voiceChannels, x => + { + Assert.NotNull(x); + Assert.NotEqual(x.Id, 0UL); + Assert.NotEqual(x.UserLimit, 0); + Assert.True(x.Bitrate > 0); + Assert.True(x.Position >= 0); + }); var voice1 = voiceChannels.Where(x => x.Name == "voice1").FirstOrDefault(); var voice2 = voiceChannels.Where(x => x.Name == "voice2").FirstOrDefault(); @@ -126,11 +131,8 @@ namespace Discord Assert.NotNull(voice1); Assert.Equal(voice1.Bitrate, 96000); Assert.Equal(voice1.Position, 1); - Assert.Equal(voice1.UserLimit, 8); Assert.NotNull(voice2); - Assert.Equal(voice2.Bitrate, 64000); - Assert.Equal(voice2.Position, 1); Assert.Equal(voice2.UserLimit, null); Assert.NotNull(voice3); From 5dda8ef31fa6e3fb536d8e168e878992532cceed Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 14:02:26 -0400 Subject: [PATCH 169/263] Added environment var fallback --- test/Discord.Net.Tests/TestConfig.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/test/Discord.Net.Tests/TestConfig.cs b/test/Discord.Net.Tests/TestConfig.cs index 21c2dcb8d..bdab13ea7 100644 --- a/test/Discord.Net.Tests/TestConfig.cs +++ b/test/Discord.Net.Tests/TestConfig.cs @@ -1,5 +1,6 @@ using Newtonsoft.Json; using System.IO; +using System; namespace Discord { @@ -12,10 +13,21 @@ namespace Discord public static TestConfig LoadFile(string path) { - using (var stream = new FileStream(path, FileMode.Open)) - using (var reader = new StreamReader(stream)) - using (var jsonReader = new JsonTextReader(reader)) - return new JsonSerializer().Deserialize(jsonReader); + if (File.Exists(path)) + { + using (var stream = new FileStream(path, FileMode.Open)) + using (var reader = new StreamReader(stream)) + using (var jsonReader = new JsonTextReader(reader)) + return new JsonSerializer().Deserialize(jsonReader); + } + else + { + return new TestConfig() + { + Token = Environment.GetEnvironmentVariable("DNET_TEST_TOKEN"), + GuildId = ulong.Parse(Environment.GetEnvironmentVariable("DNET_TEST_GUILDID")) + }; + } } } } \ No newline at end of file From 7ac02bbc40c34300bd8e51c1a310b922b5774626 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 14:05:07 -0400 Subject: [PATCH 170/263] Added credit for akavache files --- test/Discord.Net.Tests/Net/FilesystemProvider.cs | 4 ++++ test/Discord.Net.Tests/Net/HttpMixin.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/test/Discord.Net.Tests/Net/FilesystemProvider.cs b/test/Discord.Net.Tests/Net/FilesystemProvider.cs index efa56ff66..ae1b9a301 100644 --- a/test/Discord.Net.Tests/Net/FilesystemProvider.cs +++ b/test/Discord.Net.Tests/Net/FilesystemProvider.cs @@ -1,3 +1,7 @@ +//From https://github.com/akavache/Akavache +//Copyright (c) 2012 GitHub +//TODO: Remove once netstandard support is added + using Akavache; using System; using System.Collections.Generic; diff --git a/test/Discord.Net.Tests/Net/HttpMixin.cs b/test/Discord.Net.Tests/Net/HttpMixin.cs index 0c7bf2c3d..567b7df11 100644 --- a/test/Discord.Net.Tests/Net/HttpMixin.cs +++ b/test/Discord.Net.Tests/Net/HttpMixin.cs @@ -1,3 +1,7 @@ +//From https://github.com/akavache/Akavache +//Copyright (c) 2012 GitHub +//TODO: Remove once netstandard support is added + using Akavache; using System; using System.Collections.Generic; From 7c5fe79333dd95be1ec2858afe95b94e732fbfe5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 15:40:41 -0400 Subject: [PATCH 171/263] Updated build script --- build.bat | 21 ------------------- build.ps1 | 15 +++++++++++++ .../Discord.Net.Commands.csproj | 4 +++- src/Discord.Net.Core/Discord.Net.Core.csproj | 4 +++- .../Discord.Net.Providers.UdpClient.csproj | 4 +++- .../Discord.Net.Providers.WS4Net.csproj | 4 +++- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 4 +++- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 4 +++- .../Discord.Net.WebSocket.csproj | 4 +++- src/Discord.Net/Discord.Net.csproj | 4 +++- 10 files changed, 39 insertions(+), 29 deletions(-) delete mode 100644 build.bat create mode 100644 build.ps1 diff --git a/build.bat b/build.bat deleted file mode 100644 index 1de188f4e..000000000 --- a/build.bat +++ /dev/null @@ -1,21 +0,0 @@ -@echo Off -dotnet restore -dotnet pack "src\Discord.Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.API" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Core" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Commands" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Rest" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.WebSocket" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Rpc" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Providers.WS4Net" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -dotnet pack "src\Discord.Net.Providers.UDPClient" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" - -REM dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.API\Discord.Net.API.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" -REM dotnet pack "src\Discord.Net.Providers.UDPClient\Discord.Net.Providers.UDPClient.csproj" -c "%Configuration%" -o "artifacts" --version-suffix "%PrereleaseTag%" \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 000000000..e18111285 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,15 @@ +if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { + $build = [convert]::ToInt32($env:APPVEYOR_BUILD_NUMBER).ToString("00000") +} else { + $build = "dev" +} + +dotnet restore Discord.Net.sln +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" \ No newline at end of file diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 099472a34..9f0808320 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,7 +1,9 @@ A Discord.Net extension adding support for bot commands. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 582f34d80..248564295 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,7 +1,9 @@ A .Net API wrapper and bot framework for Discord. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index cf542943a..bef98b219 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -1,7 +1,9 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) net45 true Discord.Net.Providers.UDPClient diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index ee267a2c3..a5a57d5f1 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -1,7 +1,9 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) net45 true Discord.Net.Providers.WS4Net diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 753428943..ff1c196dc 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,7 +1,9 @@  A core Discord.Net library containing the REST client and models. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 9ba8e338f..b1687ade0 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,7 +1,9 @@  A core Discord.Net library containing the RPC client and models. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 03857dbe8..a1d479f3c 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,7 +1,9 @@ A core Discord.Net library containing the WebSocket client and models. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 true Discord.Net.WebSocket diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 4a01ef042..62939d3eb 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,7 +1,9 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. - 1.0.0-rc + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net discord;discordapp From a4009dca7dd63215b11c7ac868bad60fe40a8e5c Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 16:20:28 -0400 Subject: [PATCH 172/263] Abort packing if build fails --- build.ps1 | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/build.ps1 b/build.ps1 index e18111285..80d51d89f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,11 +5,15 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { } dotnet restore Discord.Net.sln -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "artifacts" -p:BuildNumber="$build" \ No newline at end of file +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } + +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" \ No newline at end of file From b5571d09ce823c71ecbe8305e9ad04c4a485cd20 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 16:33:28 -0400 Subject: [PATCH 173/263] Split build script into build, pack and test --- build.ps1 | 12 +----------- pack.ps1 | 21 +++++++++++++++++++++ test.ps1 | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 pack.ps1 create mode 100644 test.ps1 diff --git a/build.ps1 b/build.ps1 index 80d51d89f..33f1d02b4 100644 --- a/build.ps1 +++ b/build.ps1 @@ -6,14 +6,4 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { dotnet restore Discord.Net.sln if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } - -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" \ No newline at end of file +dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" \ No newline at end of file diff --git a/pack.ps1 b/pack.ps1 new file mode 100644 index 000000000..06c9347f5 --- /dev/null +++ b/pack.ps1 @@ -0,0 +1,21 @@ +if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { + $build = [convert]::ToInt32($env:APPVEYOR_BUILD_NUMBER).ToString("00000") +} else { + $build = "dev" +} + +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" \ No newline at end of file diff --git a/test.ps1 b/test.ps1 new file mode 100644 index 000000000..f82ff8672 --- /dev/null +++ b/test.ps1 @@ -0,0 +1 @@ +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj \ No newline at end of file From d043b8daea41aaae7b17340b0e95cda6eeda07cf Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 17:10:37 -0400 Subject: [PATCH 174/263] Restore tests before testing --- build.ps1 | 5 +++-- pack.ps1 | 17 +++++++++-------- test.ps1 | 5 ++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build.ps1 b/build.ps1 index 33f1d02b4..111dabbe6 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,5 +5,6 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { } dotnet restore Discord.Net.sln -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" \ No newline at end of file +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/pack.ps1 b/pack.ps1 index 06c9347f5..d9d55c8a7 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -5,17 +5,18 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { } dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" \ No newline at end of file +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/test.ps1 b/test.ps1 index f82ff8672..070bcba52 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1 +1,4 @@ -dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj \ No newline at end of file +dotnet restore test/Discord.Net.Tests/Discord.Net.Tests.csproj +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From dc659d4dcfd573e3eb07dacc754588a78e6f452d Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 17:22:47 -0400 Subject: [PATCH 175/263] Ignore warnings from HttpMixin --- test/Discord.Net.Tests/Net/HttpMixin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/Discord.Net.Tests/Net/HttpMixin.cs b/test/Discord.Net.Tests/Net/HttpMixin.cs index 567b7df11..c4a78ce0b 100644 --- a/test/Discord.Net.Tests/Net/HttpMixin.cs +++ b/test/Discord.Net.Tests/Net/HttpMixin.cs @@ -2,6 +2,8 @@ //Copyright (c) 2012 GitHub //TODO: Remove once netstandard support is added +#pragma warning disable CS0618 + using Akavache; using System; using System.Collections.Generic; From 858580a6a4479ef79cd49e3690fdb0297b0e9c5e Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 24 Jan 2017 17:30:18 -0400 Subject: [PATCH 176/263] Fixed a couple issues in the Tests csproj --- test/Discord.Net.Tests/Discord.Net.Tests.csproj | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 2968fc10e..138b15da8 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -1,15 +1,10 @@ - Exe netcoreapp1.1 $(PackageTargetFallback);portable-net45+win8+wp8+wpa81 - - - PreserveNewest - @@ -19,7 +14,7 @@ - + From 203265cb652177b2c577d62f673efc8ee68f5f07 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 12:21:58 -0400 Subject: [PATCH 177/263] Cleaned up ShardedClient, delayed connections --- ...ception.cs => WebSocketClosedException.cs} | 0 .../DiscordShardedClient.cs | 47 +++++--- .../DiscordSocketClient.cs | 106 ++++++++++-------- 3 files changed, 95 insertions(+), 58 deletions(-) rename src/Discord.Net.Core/Net/{WebSocketException.cs => WebSocketClosedException.cs} (100%) diff --git a/src/Discord.Net.Core/Net/WebSocketException.cs b/src/Discord.Net.Core/Net/WebSocketClosedException.cs similarity index 100% rename from src/Discord.Net.Core/Net/WebSocketException.cs rename to src/Discord.Net.Core/Net/WebSocketClosedException.cs diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 4bda2b479..a32c46f10 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -5,12 +5,14 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using System.Threading; namespace Discord.WebSocket { public partial class DiscordShardedClient : BaseDiscordClient, IDiscordClient { private readonly DiscordSocketConfig _baseConfig; + private readonly SemaphoreSlim _connectionGroupLock; private int[] _shardIds; private Dictionary _shardIdsToIndex; private DiscordSocketClient[] _shards; @@ -18,9 +20,9 @@ namespace Discord.WebSocket private bool _automaticShards; /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. - public int Latency { get; private set; } - internal UserStatus Status => _shards[0].Status; - internal Game? Game => _shards[0].Game; + public int Latency => GetLatency(); + public UserStatus Status => _shards[0].Status; + public Game? Game => _shards[0].Game; internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } @@ -48,6 +50,7 @@ namespace Discord.WebSocket _shardIdsToIndex = new Dictionary(); config.DisplayInitialLog = false; _baseConfig = config; + _connectionGroupLock = new SemaphoreSlim(1, 1); if (config.TotalShards == null) _automaticShards = true; @@ -61,7 +64,7 @@ namespace Discord.WebSocket _shardIdsToIndex.Add(_shardIds[i], i); var newConfig = config.Clone(); newConfig.ShardId = _shardIds[i]; - _shards[i] = new DiscordSocketClient(newConfig); + _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock); RegisterEvents(_shards[i]); } } @@ -83,7 +86,7 @@ namespace Discord.WebSocket var newConfig = _baseConfig.Clone(); newConfig.ShardId = _shardIds[i]; newConfig.TotalShards = _totalShards; - _shards[i] = new DiscordSocketClient(newConfig); + _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock); RegisterEvents(_shards[i]); } } @@ -125,12 +128,11 @@ namespace Discord.WebSocket } private async Task ConnectInternalAsync(bool waitForGuilds) { - for (int i = 0; i < _shards.Length; i++) - { - await _shards[i].ConnectAsync(waitForGuilds).ConfigureAwait(false); - if (i == 0) - CurrentUser = _shards[i].CurrentUser; - } + await Task.WhenAll( + _shards.Select(x => x.ConnectAsync(waitForGuilds)) + ).ConfigureAwait(false); + + CurrentUser = _shards[0].CurrentUser; } /// public async Task DisconnectAsync() @@ -156,11 +158,11 @@ namespace Discord.WebSocket } private int GetShardIdFor(ulong guildId) => (int)((guildId >> 22) % (uint)_totalShards); - private int GetShardIdFor(IGuild guild) + public int GetShardIdFor(IGuild guild) => GetShardIdFor(guild.Id); private DiscordSocketClient GetShardFor(ulong guildId) => GetShard(GetShardIdFor(guildId)); - private DiscordSocketClient GetShardFor(IGuild guild) + public DiscordSocketClient GetShardFor(IGuild guild) => GetShardFor(guild.Id); /// @@ -269,6 +271,14 @@ namespace Discord.WebSocket } } + private int GetLatency() + { + int total = 0; + for (int i = 0; i < _shards.Length; i++) + total += _shards[i].Latency; + return (int)Math.Round(total / (double)_shards.Length); + } + public async Task SetStatusAsync(UserStatus status) { for (int i = 0; i < _shards.Length; i++) @@ -283,6 +293,17 @@ namespace Discord.WebSocket private void RegisterEvents(DiscordSocketClient client) { client.Log += (msg) => _logEvent.InvokeAsync(msg); + client.LoggedOut += () => + { + var state = LoginState; + if (state == LoginState.LoggedIn || state == LoginState.LoggingIn) + { + //Should only happen if token is changed + var _ = LogoutAsync(); //Signal the logout, fire and forget + } + return Task.Delay(0); + }; + client.ChannelCreated += (channel) => _channelCreatedEvent.InvokeAsync(channel); client.ChannelDestroyed += (channel) => _channelDestroyedEvent.InvokeAsync(channel); client.ChannelUpdated += (oldChannel, newChannel) => _channelUpdatedEvent.InvokeAsync(oldChannel, newChannel); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 58c27dccc..7591717b6 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -17,6 +17,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using GameModel = Discord.API.Game; +using Discord.Net; namespace Discord.WebSocket { @@ -25,6 +26,7 @@ namespace Discord.WebSocket private readonly ConcurrentQueue _largeGuilds; private readonly Logger _gatewayLogger; private readonly JsonSerializer _serializer; + private readonly SemaphoreSlim _connectionGroupLock; private string _sessionId; private int _lastSeq; @@ -69,8 +71,9 @@ namespace Discord.WebSocket /// Creates a new REST/WebSocket discord client. public DiscordSocketClient() : this(new DiscordSocketConfig()) { } /// Creates a new REST/WebSocket discord client. - public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config)) { } - private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client) + public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null) { } + internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock) : this(config, CreateApiClient(config), groupLock) { } + private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock) : base(config, client) { ShardId = config.ShardId ?? 0; @@ -86,6 +89,7 @@ namespace Discord.WebSocket _nextAudioId = 1; _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); + _connectionGroupLock = groupLock; _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => @@ -171,53 +175,65 @@ namespace Discord.WebSocket if (state == ConnectionState.Connecting || state == ConnectionState.Connected) await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); - ConnectionState = ConnectionState.Connecting; - await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false); - + if (_connectionGroupLock != null) + await _connectionGroupLock.WaitAsync().ConfigureAwait(false); try { - var connectTask = new TaskCompletionSource(); - _connectTask = connectTask; - _cancelToken = new CancellationTokenSource(); - - //Abort connection on timeout - var _ = Task.Run(async () => + _canReconnect = true; + ConnectionState = ConnectionState.Connecting; + await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false); + + try { - await Task.Delay(ConnectionTimeout).ConfigureAwait(false); - connectTask.TrySetException(new TimeoutException()); - }); + var connectTask = new TaskCompletionSource(); + _connectTask = connectTask; + _cancelToken = new CancellationTokenSource(); - await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); - await ApiClient.ConnectAsync().ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); + //Abort connection on timeout + var _ = Task.Run(async () => + { + await Task.Delay(ConnectionTimeout).ConfigureAwait(false); + connectTask.TrySetException(new TimeoutException()); + }); - if (_sessionId != null) - { - await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false); - await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); - } - else - { - await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); - await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); - } + await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); + await ApiClient.ConnectAsync().ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); + await _connectedEvent.InvokeAsync().ConfigureAwait(false); - await _connectTask.Task.ConfigureAwait(false); + if (_sessionId != null) + { + await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false); + await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); + } + else + { + await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); + await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); + } - await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); - await SendStatusAsync().ConfigureAwait(false); + await _connectTask.Task.ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - if (!isReconnecting) - _canReconnect = true; - ConnectionState = ConnectionState.Connected; - await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); + + await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); + ConnectionState = ConnectionState.Connected; + await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); + } + catch (Exception) + { + await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); + throw; + } } - catch (Exception) + finally { - await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); - throw; + if (_connectionGroupLock != null) + { + await Task.Delay(5000).ConfigureAwait(false); + _connectionGroupLock.Release(); + } } } /// @@ -290,13 +306,12 @@ namespace Discord.WebSocket private async Task StartReconnectAsync(Exception ex) { - if (ex == null) - { - if (_connectTask?.TrySetCanceled() ?? false) return; - } - else + if ((ex as WebSocketClosedException).CloseCode == 4004) //Bad Token { - if (_connectTask?.TrySetException(ex) ?? false) return; + _canReconnect = false; + _connectTask?.TrySetException(ex); + await LogoutAsync().ConfigureAwait(false); + return; } await _connectionLock.WaitAsync().ConfigureAwait(false); @@ -608,6 +623,7 @@ namespace Discord.WebSocket } catch (Exception ex) { + _canReconnect = false; _connectTask.TrySetException(new Exception("Processing READY failed", ex)); return; } From 3046b62c94b6e091fb970408d3311ec038de488d Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 12:41:38 -0400 Subject: [PATCH 178/263] Cleaned up csproj/sln/scripts --- Discord.Net.sln | 14 ++++++++++++++ .../Discord.Net.Commands.csproj | 1 - src/Discord.Net.Core/Discord.Net.Core.csproj | 1 - .../Discord.Net.Providers.UdpClient.csproj | 1 - .../Discord.Net.Providers.WS4Net.csproj | 1 - src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 1 - .../Discord.Net.WebSocket.csproj | 1 - src/Discord.Net/Discord.Net.csproj | 1 - test.ps1 | 4 +--- 9 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Discord.Net.sln b/Discord.Net.sln index fbe1c9262..d5a64704e 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Providers.UdpCl EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\Discord.Net.Tests\Discord.Net.Tests.csproj", "{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -143,6 +145,18 @@ Global {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x64.Build.0 = Release|x64 {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x86.ActiveCfg = Release|x86 {ABC9F4B9-2452-4725-B522-754E0A02E282}.Release|x86.Build.0 = Release|x86 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|x64.ActiveCfg = Debug|x64 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|x64.Build.0 = Debug|x64 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|x86.ActiveCfg = Debug|x86 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Debug|x86.Build.0 = Debug|x86 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|Any CPU.Build.0 = Release|Any CPU + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x64.ActiveCfg = Release|x64 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x64.Build.0 = Release|x64 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.ActiveCfg = Release|x86 + {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 9f0808320..5e7ecda0c 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -15,7 +15,6 @@ - diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 248564295..27603b2d7 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -15,7 +15,6 @@ - diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index bef98b219..b8a278565 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index a5a57d5f1..4d73aeaa7 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index b1687ade0..1f0840d7e 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -15,7 +15,6 @@ - diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index a1d479f3c..266baa010 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 62939d3eb..6645dc956 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -15,7 +15,6 @@ - diff --git a/test.ps1 b/test.ps1 index 070bcba52..dbf15abc9 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,4 +1,2 @@ -dotnet restore test/Discord.Net.Tests/Discord.Net.Tests.csproj -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From f3ba4b6f99d5ff53192c0893e85c8bc9cabd2e26 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 15:52:18 -0400 Subject: [PATCH 179/263] Changed nupkg directory --- pack.ps1 | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pack.ps1 b/pack.ps1 index d9d55c8a7..d3d7e5679 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -4,19 +4,19 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { $build = "dev" } -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../nupkgs" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 78c892b1bb5e7516cd7c1fba1555da633d3764e8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 16:56:23 -0400 Subject: [PATCH 180/263] Pack script fixes --- build.ps1 | 4 ++-- pack.ps1 | 2 +- test.ps1 | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/build.ps1 b/build.ps1 index 111dabbe6..b39b817cf 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,10 +1,10 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $build = [convert]::ToInt32($env:APPVEYOR_BUILD_NUMBER).ToString("00000") + $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") } else { $build = "dev" } -dotnet restore Discord.Net.sln +appveyor-retry dotnet restore Discord.Net.sln -v Minimal -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/pack.ps1 b/pack.ps1 index d3d7e5679..531623a30 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,5 +1,5 @@ if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $build = [convert]::ToInt32($env:APPVEYOR_BUILD_NUMBER).ToString("00000") + $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") } else { $build = "dev" } diff --git a/test.ps1 b/test.ps1 index dbf15abc9..98df9469d 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,2 +1,8 @@ +if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { + $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") +} else { + $build = "dev" +} + dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild -p:BuildNumber="$build" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 7e9491fb34f7d28810704748963898f285798eb0 Mon Sep 17 00:00:00 2001 From: firebingo Date: Wed, 4 Jan 2017 14:52:15 -0700 Subject: [PATCH 181/263] Fix Issue #446. RestUserMessage initializes _reactions even if there are no reactions so get doesn't cause exception. --- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 2d13ed3f7..b064e67b0 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -115,6 +115,8 @@ namespace Discord.Rest else _reactions = ImmutableArray.Create(); } + else + _reactions = ImmutableArray.Create(); if (model.Content.IsSpecified) { From b0db37cad661f2463ca6ce90d7bd567d593d2293 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 18:00:00 -0400 Subject: [PATCH 182/263] Versions are hard --- build.ps1 | 10 ++------- init.ps1 | 5 +++++ pack.ps1 | 22 +++++++------------ .../Discord.Net.Commands.csproj | 3 +-- src/Discord.Net.Core/Discord.Net.Core.csproj | 3 +-- .../Discord.Net.Providers.UdpClient.csproj | 3 +-- .../Discord.Net.Providers.WS4Net.csproj | 3 +-- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 3 +-- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 3 +-- .../Discord.Net.WebSocket.csproj | 3 +-- src/Discord.Net/Discord.Net.csproj | 3 +-- test.ps1 | 8 +------ 12 files changed, 24 insertions(+), 45 deletions(-) create mode 100644 init.ps1 diff --git a/build.ps1 b/build.ps1 index b39b817cf..437d1ab18 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,10 +1,4 @@ -if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") -} else { - $build = "dev" -} - -appveyor-retry dotnet restore Discord.Net.sln -v Minimal -p:BuildNumber="$build" +appveyor-retry dotnet restore Discord.Net.sln -v Minimal -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet build Discord.Net.sln -c "Release" -p:BuildNumber="$build" +dotnet build Discord.Net.sln -c "Release" -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/init.ps1 b/init.ps1 new file mode 100644 index 000000000..b57464228 --- /dev/null +++ b/init.ps1 @@ -0,0 +1,5 @@ +if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { + $env:BUILD = "rc-$($env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0"))" +} else { + $env:BUILD = "dev" +} \ No newline at end of file diff --git a/pack.ps1 b/pack.ps1 index 531623a30..e6c593104 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,22 +1,16 @@ -if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") -} else { - $build = "dev" -} - -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build -p:BuildNumber="$build" +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 5e7ecda0c..1736bfcdb 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -2,8 +2,7 @@ A Discord.Net extension adding support for bot commands. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 27603b2d7..fe8f3a2a4 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -2,8 +2,7 @@ A .Net API wrapper and bot framework for Discord. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index b8a278565..3e3b9a5fe 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -2,8 +2,7 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient 1.0.0 - rc-dev - rc-$(BuildNumber) + dev net45 true Discord.Net.Providers.UDPClient diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index 4d73aeaa7..83a3669d9 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -2,8 +2,7 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net 1.0.0 - rc-dev - rc-$(BuildNumber) + dev net45 true Discord.Net.Providers.WS4Net diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index ff1c196dc..618e3b54a 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -2,8 +2,7 @@ A core Discord.Net library containing the REST client and models. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 1f0840d7e..0613cd846 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -2,8 +2,7 @@ A core Discord.Net library containing the RPC client and models. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 266baa010..f14a5b874 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -2,8 +2,7 @@ A core Discord.Net library containing the WebSocket client and models. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 true Discord.Net.WebSocket diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 6645dc956..fd94dab20 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -2,8 +2,7 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0 - rc-dev - rc-$(BuildNumber) + dev netstandard1.1;netstandard1.3 Discord.Net discord;discordapp diff --git a/test.ps1 b/test.ps1 index 98df9469d..34fe4aaa0 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,8 +1,2 @@ -if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $build = $env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0") -} else { - $build = "dev" -} - -dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild -p:BuildNumber="$build" +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild -p:VersionSuffix="$env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 24663605f6d878b06b7027c046b403276c672883 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 20:14:19 -0400 Subject: [PATCH 183/263] Fixed CI detecting all packages as dev --- build.ps1 | 4 +-- init.ps1 | 5 --- pack.ps1 | 33 ++++++++++--------- .../Discord.Net.Commands.csproj | 3 +- src/Discord.Net.Core/Discord.Net.Core.csproj | 3 +- .../Discord.Net.Providers.UdpClient.csproj | 3 +- .../Discord.Net.Providers.WS4Net.csproj | 3 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 3 +- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 3 +- .../Discord.Net.WebSocket.csproj | 3 +- src/Discord.Net/Discord.Net.csproj | 3 +- test.ps1 | 2 +- 12 files changed, 36 insertions(+), 32 deletions(-) delete mode 100644 init.ps1 diff --git a/build.ps1 b/build.ps1 index 437d1ab18..08508bbcf 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,4 +1,4 @@ -appveyor-retry dotnet restore Discord.Net.sln -v Minimal -p:VersionSuffix="$env:BUILD" +appveyor-retry dotnet restore Discord.Net.sln -v Minimal /p:BuildNumber="$Env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet build Discord.Net.sln -c "Release" -p:VersionSuffix="$env:BUILD" +dotnet build Discord.Net.sln -c "Release" /p:BuildNumber="$Env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/init.ps1 b/init.ps1 deleted file mode 100644 index b57464228..000000000 --- a/init.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -if (Test-Path Env:\APPVEYOR_BUILD_NUMBER) { - $env:BUILD = "rc-$($env:APPVEYOR_BUILD_NUMBER.PadLeft(5, "0"))" -} else { - $env:BUILD = "dev" -} \ No newline at end of file diff --git a/pack.ps1 b/pack.ps1 index e6c593104..89335a2ac 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,16 +1,17 @@ -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build -p:VersionSuffix="$env:BUILD" -if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file +#dotnet restore "src\Discord.Net\Discord.Net.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Core\Discord.Net.Core.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Commands\Discord.Net.Commands.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Rest\Discord.Net.Rest.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" /p:BuildNumber="$Env:BUILD" +#dotnet restore "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" /p:BuildNumber="$Env:BUILD" + +dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" \ No newline at end of file diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 1736bfcdb..5e7ecda0c 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -2,7 +2,8 @@ A Discord.Net extension adding support for bot commands. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index fe8f3a2a4..27603b2d7 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -2,7 +2,8 @@ A .Net API wrapper and bot framework for Discord. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index 3e3b9a5fe..b8a278565 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -2,7 +2,8 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient 1.0.0 - dev + rc-dev + rc-$(BuildNumber) net45 true Discord.Net.Providers.UDPClient diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index 83a3669d9..4d73aeaa7 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -2,7 +2,8 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net 1.0.0 - dev + rc-dev + rc-$(BuildNumber) net45 true Discord.Net.Providers.WS4Net diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 618e3b54a..ff1c196dc 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -2,7 +2,8 @@ A core Discord.Net library containing the REST client and models. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 0613cd846..1f0840d7e 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -2,7 +2,8 @@ A core Discord.Net library containing the RPC client and models. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index f14a5b874..266baa010 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -2,7 +2,8 @@ A core Discord.Net library containing the WebSocket client and models. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 true Discord.Net.WebSocket diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index fd94dab20..6645dc956 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -2,7 +2,8 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. 1.0.0 - dev + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net discord;discordapp diff --git a/test.ps1 b/test.ps1 index 34fe4aaa0..0cbfc9cda 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,2 +1,2 @@ -dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild -p:VersionSuffix="$env:BUILD" +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild /p:BuildNumber="$Env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 3bc25200957e7d6f2f677dc9d78e0565a3961a79 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 20:39:04 -0400 Subject: [PATCH 184/263] Abandon VersionSuffix --- src/Discord.Net.Commands/Discord.Net.Commands.csproj | 5 ++--- src/Discord.Net.Core/Discord.Net.Core.csproj | 5 ++--- .../Discord.Net.Providers.UdpClient.csproj | 5 ++--- .../Discord.Net.Providers.WS4Net.csproj | 5 ++--- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 5 ++--- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 5 ++--- src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj | 5 ++--- src/Discord.Net/Discord.Net.csproj | 5 ++--- 8 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 5e7ecda0c..f670b8901 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,9 +1,8 @@ A Discord.Net extension adding support for bot commands. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 27603b2d7..9806c909f 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,9 +1,8 @@ A .Net API wrapper and bot framework for Discord. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index b8a278565..42b0527b3 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -1,9 +1,8 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) net45 true Discord.Net.Providers.UDPClient diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index 4d73aeaa7..b84ddcbbb 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -1,9 +1,8 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) net45 true Discord.Net.Providers.WS4Net diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index ff1c196dc..e2f3c98c5 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,9 +1,8 @@  A core Discord.Net library containing the REST client and models. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 1f0840d7e..ce79a70ba 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,9 +1,8 @@  A core Discord.Net library containing the RPC client and models. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 266baa010..3b7ecaa9d 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,9 +1,8 @@ A core Discord.Net library containing the WebSocket client and models. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 true Discord.Net.WebSocket diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index 6645dc956..b9a51af03 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,9 +1,8 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. - 1.0.0 - rc-dev - rc-$(BuildNumber) + 1.0.0-rc-dev + 1.0.0-rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net discord;discordapp From eb2a946dd6183d32bdb9c6d5e73d6a1f7a449cd5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 22:27:24 -0400 Subject: [PATCH 185/263] Updated to 1.0.0-rc3 tooling --- pack.ps1 | 9 --------- src/Discord.Net.Commands/Discord.Net.Commands.csproj | 9 ++++----- src/Discord.Net.Core/Discord.Net.Core.csproj | 9 ++++----- .../Discord.Net.Providers.UdpClient.csproj | 10 ++++------ .../Discord.Net.Providers.WS4Net.csproj | 9 ++++----- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 10 ++++------ src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 9 ++++----- .../Discord.Net.WebSocket.csproj | 11 +++++------ src/Discord.Net/Discord.Net.csproj | 9 ++++----- test.ps1 | 2 +- test/Discord.Net.Tests/Discord.Net.Tests.csproj | 6 ++---- 11 files changed, 36 insertions(+), 57 deletions(-) diff --git a/pack.ps1 b/pack.ps1 index 89335a2ac..2fda5dac4 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,12 +1,3 @@ -#dotnet restore "src\Discord.Net\Discord.Net.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Core\Discord.Net.Core.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Commands\Discord.Net.Commands.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Rest\Discord.Net.Rest.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" /p:BuildNumber="$Env:BUILD" -#dotnet restore "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" /p:BuildNumber="$Env:BUILD" - dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index f670b8901..0d236aee3 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,8 +1,9 @@ A Discord.Net extension adding support for bot commands. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Commands discord;discordapp @@ -11,10 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord.Commands + true - - - diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 9806c909f..edd344a2e 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,8 +1,9 @@ A .Net API wrapper and bot framework for Discord. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Core discord;discordapp @@ -11,10 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord + true - - - diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index 42b0527b3..9e868c68a 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -1,10 +1,10 @@ An optional UDP client provider for Discord.Net using System.Net.UdpClient - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) net45 - true Discord.Net.Providers.UDPClient discord;discordapp https://github.com/RogueException/Discord.Net @@ -12,10 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord.Providers.UDPClient + true - - - diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index b84ddcbbb..52e12d587 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -1,8 +1,9 @@ An optional WebSocket client provider for Discord.Net using WebSocket4Net - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) net45 true Discord.Net.Providers.WS4Net @@ -12,10 +13,8 @@ git git://github.com/RogueException/Discord.Net Discord.Providers.WS4Net + true - - - diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index e2f3c98c5..57765dc97 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,8 +1,9 @@  A core Discord.Net library containing the REST client and models. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rest discord;discordapp @@ -11,11 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord.Rest + true - - - - diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index ce79a70ba..35309d6ad 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,8 +1,9 @@  A core Discord.Net library containing the RPC client and models. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rpc discord;discordapp @@ -11,10 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord.Rpc + true - - - Net\DefaultWebSocketClient.cs diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 3b7ecaa9d..e1e44d364 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,10 +1,10 @@ A core Discord.Net library containing the WebSocket client and models. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 - true Discord.Net.WebSocket discord;discordapp https://github.com/RogueException/Discord.Net @@ -12,10 +12,9 @@ git git://github.com/RogueException/Discord.Net Discord.WebSocket + true + true - - - diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj index b9a51af03..5dba63ece 100644 --- a/src/Discord.Net/Discord.Net.csproj +++ b/src/Discord.Net/Discord.Net.csproj @@ -1,8 +1,9 @@ An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. - 1.0.0-rc-dev - 1.0.0-rc-$(BuildNumber) + 1.0.0 + rc-dev + rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net discord;discordapp @@ -11,10 +12,8 @@ git git://github.com/RogueException/Discord.Net Discord + true - - - diff --git a/test.ps1 b/test.ps1 index 0cbfc9cda..b8a817743 100644 --- a/test.ps1 +++ b/test.ps1 @@ -1,2 +1,2 @@ -dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --noBuild /p:BuildNumber="$Env:BUILD" +dotnet test test/Discord.Net.Tests/Discord.Net.Tests.csproj -c "Release" --no-build /p:BuildNumber="$Env:BUILD" if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 138b15da8..ec0391f2f 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -1,11 +1,10 @@ + Exe netcoreapp1.1 $(PackageTargetFallback);portable-net45+win8+wp8+wpa81 + Discord - - - @@ -13,7 +12,6 @@ - From 224af65b295e5bb2fc09ef7dbeeb14b3a0b1a559 Mon Sep 17 00:00:00 2001 From: RogueException Date: Wed, 25 Jan 2017 23:02:14 -0400 Subject: [PATCH 186/263] Updated README with new compile requirements --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 934cb775c..d4e975f49 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![MyGet Build Status](https://www.myget.org/BuildSource/Badge/discord-net?identifier=15bf7c42-22dd-4406-93e5-3cafc62bbc85)](https://www.myget.org/) [![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/0SBTUU1wZTVjAMPx) -An unofficial .Net API Wrapper for the Discord client (http://discordapp.com). +An unofficial .NET API Wrapper for the Discord client (http://discordapp.com). Check out the [documentation](https://discord.foxbot.me/docs/) or join the [Discord API Chat](https://discord.gg/0SBTUU1wZTVjAMPx). @@ -29,15 +29,17 @@ Nightly builds are available through our MyGet feed (`https://www.myget.org/F/di In order to compile Discord.Net, you require the following: ### Using Visual Studio -- [Visual Studio 2017 RC Build 26014.0](https://www.microsoft.com/net/core#windowsvs2017) +- [Visual Studio 2017 RC](https://www.microsoft.com/net/core#windowsvs2017) +- [.NET Core SDK 1.0 RC3](https://github.com/dotnet/core/blob/master/release-notes/rc3-download.md) The .NET Core and Docker (Preview) workload is required during Visual Studio installation. ### Using Command Line -- [.Net Core 1.1 SDK](https://www.microsoft.com/net/download/core) +- [.NET Core 1.1 Runtime](https://www.microsoft.com/net/download/core) +- [.NET Core SDK 1.0 RC3](https://github.com/dotnet/core/blob/master/release-notes/rc3-download.md) ## Known Issues ### WebSockets (Win7 and earlier) -.Net Core 1.1 does not support WebSockets on Win7 and earlier. It's recommended to use the Discord.Net.Providers.WS4Net package until this is resolved. +.NET Core 1.1 does not support WebSockets on Win7 and earlier. It's recommended to use the Discord.Net.Providers.WS4Net package until this is resolved. Track the issue [here](https://github.com/dotnet/corefx/issues/9503). From c10ebeef57274c352ed0fd6edb6d467f387732d8 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 00:06:47 -0400 Subject: [PATCH 187/263] Updated xunit --- test/Discord.Net.Tests/Discord.Net.Tests.csproj | 10 ++++++++-- test/Discord.Net.Tests/xunit.runner.json | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 test/Discord.Net.Tests/xunit.runner.json diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index ec0391f2f..986605eeb 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -5,6 +5,11 @@ $(PackageTargetFallback);portable-net45+win8+wp8+wpa81 Discord + + + PreserveNewest + + @@ -14,7 +19,8 @@ - - + + + diff --git a/test/Discord.Net.Tests/xunit.runner.json b/test/Discord.Net.Tests/xunit.runner.json new file mode 100644 index 000000000..ac3e63046 --- /dev/null +++ b/test/Discord.Net.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "diagnosticMessages": true, + "methodDisplay": "classAndMethod" +} \ No newline at end of file From dcde84616323d9c60ef5b6fc61111a9b51c98eaa Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 00:28:39 -0400 Subject: [PATCH 188/263] Added build status to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4e975f49..17ffbc793 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Discord.Net v1.0.0-rc [![MyGet](https://img.shields.io/myget/discord-net/vpre/Discord.Net.svg)](https://www.myget.org/feed/Packages/discord-net) -[![MyGet Build Status](https://www.myget.org/BuildSource/Badge/discord-net?identifier=15bf7c42-22dd-4406-93e5-3cafc62bbc85)](https://www.myget.org/) +[![Build status](https://ci.appveyor.com/api/projects/status/5sb7n8a09w9clute/branch/dev?svg=true)](https://ci.appveyor.com/project/RogueException/discord-net/branch/dev) [![Discord](https://discordapp.com/api/guilds/81384788765712384/widget.png)](https://discord.gg/0SBTUU1wZTVjAMPx) An unofficial .NET API Wrapper for the Discord client (http://discordapp.com). From 34139c9b59e06fcfb497304def96b66554135bdc Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 10:43:32 -0400 Subject: [PATCH 189/263] Converted Discord.Net to a metapackage --- Discord.Net.sln | 14 ----------- pack.ps1 | 7 +++--- .../Discord.Net.Commands.csproj | 3 ++- src/Discord.Net.Core/Discord.Net.Core.csproj | 3 ++- .../Discord.Net.Providers.UdpClient.csproj | 3 ++- .../Discord.Net.Providers.WS4Net.csproj | 3 ++- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 3 ++- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 3 ++- .../Discord.Net.WebSocket.csproj | 3 ++- src/Discord.Net/Discord.Net.csproj | 24 ------------------- src/Discord.Net/Discord.Net.nuspec | 22 +++++++++++++++++ 11 files changed, 40 insertions(+), 48 deletions(-) delete mode 100644 src/Discord.Net/Discord.Net.csproj create mode 100644 src/Discord.Net/Discord.Net.nuspec diff --git a/Discord.Net.sln b/Discord.Net.sln index d5a64704e..25b6e0386 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26014.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net", "src\Discord.Net\Discord.Net.csproj", "{496DB20A-A455-4D01-B6BC-90FE6D7C6B81}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Impls", "Impls", "{288C363D-A636-4EAE-9AC1-4698B641B26E}" @@ -37,18 +35,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|Any CPU.Build.0 = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x64.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x64.Build.0 = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Debug|x86.Build.0 = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.ActiveCfg = Release|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|Any CPU.Build.0 = Release|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x64.Build.0 = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x86.ActiveCfg = Debug|Any CPU - {496DB20A-A455-4D01-B6BC-90FE6D7C6B81}.Release|x86.Build.0 = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|Any CPU.Build.0 = Debug|Any CPU {91E9E7BD-75C9-4E98-84AA-2C271922E5C2}.Debug|x64.ActiveCfg = Debug|x64 diff --git a/pack.ps1 b/pack.ps1 index 2fda5dac4..9ef52fc3a 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,8 +1,9 @@ -dotnet pack "src\Discord.Net\Discord.Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" -dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" -dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" \ No newline at end of file +dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" + +call nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties build="$Env:BUILD" \ No newline at end of file diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index 0d236aee3..452b52f21 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,11 +1,12 @@ - A Discord.Net extension adding support for bot commands. 1.0.0 rc-dev rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Commands + RogueException + A Discord.Net extension adding support for bot commands. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index edd344a2e..9f75d7327 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,11 +1,12 @@ - A .Net API wrapper and bot framework for Discord. 1.0.0 rc-dev rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Core + RogueException + A .Net API wrapper and bot framework for Discord. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj index 9e868c68a..984cd8f9c 100644 --- a/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj +++ b/src/Discord.Net.Providers.UdpClient/Discord.Net.Providers.UdpClient.csproj @@ -1,11 +1,12 @@ - An optional UDP client provider for Discord.Net using System.Net.UdpClient 1.0.0 rc-dev rc-$(BuildNumber) net45 Discord.Net.Providers.UDPClient + RogueException + An optional UDP client provider for Discord.Net using System.Net.UdpClient discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj index 52e12d587..62adfc0b1 100644 --- a/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj +++ b/src/Discord.Net.Providers.WS4Net/Discord.Net.Providers.WS4Net.csproj @@ -1,12 +1,13 @@ - An optional WebSocket client provider for Discord.Net using WebSocket4Net 1.0.0 rc-dev rc-$(BuildNumber) net45 true Discord.Net.Providers.WS4Net + RogueException + An optional WebSocket client provider for Discord.Net using WebSocket4Net discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 57765dc97..b7495f273 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,11 +1,12 @@  - A core Discord.Net library containing the REST client and models. 1.0.0 rc-dev rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rest + RogueException + A core Discord.Net library containing the REST client and models. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 35309d6ad..628c8e032 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -1,11 +1,12 @@  - A core Discord.Net library containing the RPC client and models. 1.0.0 rc-dev rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.Rpc + RogueException + A core Discord.Net library containing the RPC client and models. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index e1e44d364..b5dab98e5 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,11 +1,12 @@ - A core Discord.Net library containing the WebSocket client and models. 1.0.0 rc-dev rc-$(BuildNumber) netstandard1.1;netstandard1.3 Discord.Net.WebSocket + RogueException + A core Discord.Net library containing the WebSocket client and models. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net/Discord.Net.csproj b/src/Discord.Net/Discord.Net.csproj deleted file mode 100644 index 5dba63ece..000000000 --- a/src/Discord.Net/Discord.Net.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. - 1.0.0 - rc-dev - rc-$(BuildNumber) - netstandard1.1;netstandard1.3 - Discord.Net - discord;discordapp - https://github.com/RogueException/Discord.Net - http://opensource.org/licenses/MIT - git - git://github.com/RogueException/Discord.Net - Discord - true - - - - - - - - - \ No newline at end of file diff --git a/src/Discord.Net/Discord.Net.nuspec b/src/Discord.Net/Discord.Net.nuspec new file mode 100644 index 000000000..0d5061455 --- /dev/null +++ b/src/Discord.Net/Discord.Net.nuspec @@ -0,0 +1,22 @@ + + + + Discord.Net + 1.0.0-rc-$build$ + Discord.Net + RogueException + RogueException + An aynchronous API wrapper for Discord. This metapackage includes all of the optional Discord.Net components. + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + false + + + + + + + + + \ No newline at end of file From 4610e66fdf8987d7b89371fef78ce83bdea03d03 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 11:06:06 -0400 Subject: [PATCH 190/263] Pack script fixes --- pack.ps1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pack.ps1 b/pack.ps1 index 9ef52fc3a..0f84ea309 100644 --- a/pack.ps1 +++ b/pack.ps1 @@ -1,9 +1,17 @@ dotnet pack "src\Discord.Net.Core\Discord.Net.Core.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Rest\Discord.Net.Rest.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.WebSocket\Discord.Net.WebSocket.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Rpc\Discord.Net.Rpc.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Commands\Discord.Net.Commands.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Providers.WS4Net\Discord.Net.Providers.WS4Net.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } dotnet pack "src\Discord.Net.Providers.UdpClient\Discord.Net.Providers.UdpClient.csproj" -c "Release" -o "../../artifacts" --no-build /p:BuildNumber="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } -call nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties build="$Env:BUILD" \ No newline at end of file +nuget pack src\Discord.Net\Discord.Net.nuspec -OutputDirectory "artifacts" -properties build="$Env:BUILD" +if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) } \ No newline at end of file From 56b260c5ea714cef453419cd198280979de34b5c Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 11:10:04 -0400 Subject: [PATCH 191/263] Removed .Net Core runtime req from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 17ffbc793..903fa76c1 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,6 @@ In order to compile Discord.Net, you require the following: The .NET Core and Docker (Preview) workload is required during Visual Studio installation. ### Using Command Line -- [.NET Core 1.1 Runtime](https://www.microsoft.com/net/download/core) - [.NET Core SDK 1.0 RC3](https://github.com/dotnet/core/blob/master/release-notes/rc3-download.md) ## Known Issues From b4373b5a94dd2bfa1eb3651d4517cbc14228a085 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 11:15:23 -0400 Subject: [PATCH 192/263] Added dependency groups to metapackage nuspec --- src/Discord.Net/Discord.Net.nuspec | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net/Discord.Net.nuspec b/src/Discord.Net/Discord.Net.nuspec index 0d5061455..3b9d2da56 100644 --- a/src/Discord.Net/Discord.Net.nuspec +++ b/src/Discord.Net/Discord.Net.nuspec @@ -12,11 +12,20 @@ http://opensource.org/licenses/MIT false - - - - - + + + + + + + + + + + + + + \ No newline at end of file From 99b1398dc44672e12f9d772aaa8182b3b7e581d1 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 26 Jan 2017 11:23:04 -0400 Subject: [PATCH 193/263] I forgot what frameworks we target... --- src/Discord.Net/Discord.Net.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net/Discord.Net.nuspec b/src/Discord.Net/Discord.Net.nuspec index 3b9d2da56..9966d9d23 100644 --- a/src/Discord.Net/Discord.Net.nuspec +++ b/src/Discord.Net/Discord.Net.nuspec @@ -12,7 +12,7 @@ http://opensource.org/licenses/MIT false - + From 6a0f41a4dc7e0ec234f465aef1e935caac3142ea Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 16 Jan 2017 13:21:32 -0500 Subject: [PATCH 194/263] Add voice guide --- docs/api/.manifest | 2 +- docs/guides/commands.md | 8 +- docs/guides/intro.md | 4 +- docs/guides/samples/audio_create_ffmpeg.cs | 11 +++ docs/guides/samples/audio_ffmpeg.cs | 9 ++ docs/guides/samples/joining_audio.cs | 11 +-- docs/guides/voice.md | 97 ++++++++++++++++++++-- 7 files changed, 121 insertions(+), 21 deletions(-) create mode 100644 docs/guides/samples/audio_create_ffmpeg.cs create mode 100644 docs/guides/samples/audio_ffmpeg.cs diff --git a/docs/api/.manifest b/docs/api/.manifest index 9b0080765..0a47304c4 100644 --- a/docs/api/.manifest +++ b/docs/api/.manifest @@ -1 +1 @@ -{"Discord":"Discord.yml","Discord.ConnectionState":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnected":"Discord.ConnectionState.yml","Discord.ConnectionState.Connecting":"Discord.ConnectionState.yml","Discord.ConnectionState.Connected":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnecting":"Discord.ConnectionState.yml","Discord.DiscordConfig":"Discord.DiscordConfig.yml","Discord.DiscordConfig.APIVersion":"Discord.DiscordConfig.yml","Discord.DiscordConfig.Version":"Discord.DiscordConfig.yml","Discord.DiscordConfig.ClientAPIUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.CDNUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.InviteUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.DefaultRequestTimeout":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessageSize":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessagesPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxUsersPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.LogLevel":"Discord.DiscordConfig.yml","Discord.Format":"Discord.Format.yml","Discord.Format.Bold(System.String)":"Discord.Format.yml","Discord.Format.Italics(System.String)":"Discord.Format.yml","Discord.Format.Underline(System.String)":"Discord.Format.yml","Discord.Format.Strikethrough(System.String)":"Discord.Format.yml","Discord.Format.Code(System.String,System.String)":"Discord.Format.yml","Discord.Format.Sanitize(System.String)":"Discord.Format.yml","Discord.IDiscordClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectionState":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ApiClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CurrentUser":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.DisconnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetApplicationInfoAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetConnectionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetInviteAsync(System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.String,System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionAsync(System.String)":"Discord.IDiscordClient.yml","Discord.LoginState":"Discord.LoginState.yml","Discord.LoginState.LoggedOut":"Discord.LoginState.yml","Discord.LoginState.LoggingIn":"Discord.LoginState.yml","Discord.LoginState.LoggedIn":"Discord.LoginState.yml","Discord.LoginState.LoggingOut":"Discord.LoginState.yml","Discord.RequestOptions":"Discord.RequestOptions.yml","Discord.RequestOptions.Default":"Discord.RequestOptions.yml","Discord.RequestOptions.Timeout":"Discord.RequestOptions.yml","Discord.RequestOptions.HeaderOnly":"Discord.RequestOptions.yml","Discord.RequestOptions.#ctor":"Discord.RequestOptions.yml","Discord.RequestOptions.Clone":"Discord.RequestOptions.yml","Discord.TokenType":"Discord.TokenType.yml","Discord.TokenType.User":"Discord.TokenType.yml","Discord.TokenType.Bearer":"Discord.TokenType.yml","Discord.TokenType.Bot":"Discord.TokenType.yml","Discord.CacheMode":"Discord.CacheMode.yml","Discord.CacheMode.AllowDownload":"Discord.CacheMode.yml","Discord.CacheMode.CacheOnly":"Discord.CacheMode.yml","Discord.IApplication":"Discord.IApplication.yml","Discord.IApplication.Name":"Discord.IApplication.yml","Discord.IApplication.Description":"Discord.IApplication.yml","Discord.IApplication.RPCOrigins":"Discord.IApplication.yml","Discord.IApplication.Flags":"Discord.IApplication.yml","Discord.IApplication.IconUrl":"Discord.IApplication.yml","Discord.IApplication.Owner":"Discord.IApplication.yml","Discord.IDeletable":"Discord.IDeletable.yml","Discord.IDeletable.DeleteAsync(Discord.RequestOptions)":"Discord.IDeletable.yml","Discord.IEntity`1":"Discord.IEntity-1.yml","Discord.IEntity`1.Id":"Discord.IEntity-1.yml","Discord.IMentionable":"Discord.IMentionable.yml","Discord.IMentionable.Mention":"Discord.IMentionable.yml","Discord.ISnowflakeEntity":"Discord.ISnowflakeEntity.yml","Discord.ISnowflakeEntity.CreatedAt":"Discord.ISnowflakeEntity.yml","Discord.IUpdateable":"Discord.IUpdateable.yml","Discord.IUpdateable.UpdateAsync(Discord.RequestOptions)":"Discord.IUpdateable.yml","Discord.ChannelType":"Discord.ChannelType.yml","Discord.ChannelType.Text":"Discord.ChannelType.yml","Discord.ChannelType.DM":"Discord.ChannelType.yml","Discord.ChannelType.Voice":"Discord.ChannelType.yml","Discord.ChannelType.Group":"Discord.ChannelType.yml","Discord.IAudioChannel":"Discord.IAudioChannel.yml","Discord.IChannel":"Discord.IChannel.yml","Discord.IChannel.Name":"Discord.IChannel.yml","Discord.IChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IDMChannel":"Discord.IDMChannel.yml","Discord.IDMChannel.Recipient":"Discord.IDMChannel.yml","Discord.IDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.IDMChannel.yml","Discord.IGroupChannel":"Discord.IGroupChannel.yml","Discord.IGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.IGroupChannel.yml","Discord.IGuildChannel":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Position":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Guild":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GuildId":"Discord.IGuildChannel.yml","Discord.IGuildChannel.PermissionOverwrites":"Discord.IGuildChannel.yml","Discord.IGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IMessageChannel":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.EnterTypingState(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IPrivateChannel":"Discord.IPrivateChannel.yml","Discord.IPrivateChannel.Recipients":"Discord.IPrivateChannel.yml","Discord.ITextChannel":"Discord.ITextChannel.yml","Discord.ITextChannel.Topic":"Discord.ITextChannel.yml","Discord.ITextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.ITextChannel.yml","Discord.IVoiceChannel":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.Bitrate":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.UserLimit":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ConnectAsync":"Discord.IVoiceChannel.yml","Discord.DefaultMessageNotifications":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.AllMessages":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.MentionsOnly":"Discord.DefaultMessageNotifications.yml","Discord.GuildEmoji":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Id":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Name":"Discord.GuildEmoji.yml","Discord.GuildEmoji.IsManaged":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RequireColons":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RoleIds":"Discord.GuildEmoji.yml","Discord.GuildEmoji.ToString":"Discord.GuildEmoji.yml","Discord.IBan":"Discord.IBan.yml","Discord.IBan.User":"Discord.IBan.yml","Discord.IBan.Reason":"Discord.IBan.yml","Discord.IGuild":"Discord.IGuild.yml","Discord.IGuild.Name":"Discord.IGuild.yml","Discord.IGuild.AFKTimeout":"Discord.IGuild.yml","Discord.IGuild.IsEmbeddable":"Discord.IGuild.yml","Discord.IGuild.DefaultMessageNotifications":"Discord.IGuild.yml","Discord.IGuild.MfaLevel":"Discord.IGuild.yml","Discord.IGuild.VerificationLevel":"Discord.IGuild.yml","Discord.IGuild.IconId":"Discord.IGuild.yml","Discord.IGuild.IconUrl":"Discord.IGuild.yml","Discord.IGuild.SplashId":"Discord.IGuild.yml","Discord.IGuild.SplashUrl":"Discord.IGuild.yml","Discord.IGuild.Available":"Discord.IGuild.yml","Discord.IGuild.AFKChannelId":"Discord.IGuild.yml","Discord.IGuild.DefaultChannelId":"Discord.IGuild.yml","Discord.IGuild.EmbedChannelId":"Discord.IGuild.yml","Discord.IGuild.OwnerId":"Discord.IGuild.yml","Discord.IGuild.VoiceRegionId":"Discord.IGuild.yml","Discord.IGuild.AudioClient":"Discord.IGuild.yml","Discord.IGuild.EveryoneRole":"Discord.IGuild.yml","Discord.IGuild.Emojis":"Discord.IGuild.yml","Discord.IGuild.Features":"Discord.IGuild.yml","Discord.IGuild.Roles":"Discord.IGuild.yml","Discord.IGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.LeaveAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetBansAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetRole(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.DownloadUsersAsync":"Discord.IGuild.yml","Discord.IGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuildIntegration":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Id":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Name":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Type":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsEnabled":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsSyncing":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireBehavior":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireGracePeriod":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.SyncedAt":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Account":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Guild":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.GuildId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.RoleId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.User":"Discord.IGuildIntegration.yml","Discord.IntegrationAccount":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Id":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Name":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.ToString":"Discord.IntegrationAccount.yml","Discord.IUserGuild":"Discord.IUserGuild.yml","Discord.IUserGuild.Name":"Discord.IUserGuild.yml","Discord.IUserGuild.IconUrl":"Discord.IUserGuild.yml","Discord.IUserGuild.IsOwner":"Discord.IUserGuild.yml","Discord.IUserGuild.Permissions":"Discord.IUserGuild.yml","Discord.IVoiceRegion":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Id":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Name":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsVip":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsOptimal":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SampleHostname":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SamplePort":"Discord.IVoiceRegion.yml","Discord.MfaLevel":"Discord.MfaLevel.yml","Discord.MfaLevel.Disabled":"Discord.MfaLevel.yml","Discord.MfaLevel.Enabled":"Discord.MfaLevel.yml","Discord.VerificationLevel":"Discord.VerificationLevel.yml","Discord.VerificationLevel.None":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Low":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Medium":"Discord.VerificationLevel.yml","Discord.VerificationLevel.High":"Discord.VerificationLevel.yml","Discord.IInvite":"Discord.IInvite.yml","Discord.IInvite.Code":"Discord.IInvite.yml","Discord.IInvite.Url":"Discord.IInvite.yml","Discord.IInvite.Channel":"Discord.IInvite.yml","Discord.IInvite.ChannelId":"Discord.IInvite.yml","Discord.IInvite.Guild":"Discord.IInvite.yml","Discord.IInvite.GuildId":"Discord.IInvite.yml","Discord.IInvite.AcceptAsync(Discord.RequestOptions)":"Discord.IInvite.yml","Discord.IInviteMetadata":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Inviter":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsRevoked":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsTemporary":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxAge":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxUses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Uses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.CreatedAt":"Discord.IInviteMetadata.yml","Discord.Direction":"Discord.Direction.yml","Discord.Direction.Before":"Discord.Direction.yml","Discord.Direction.After":"Discord.Direction.yml","Discord.Direction.Around":"Discord.Direction.yml","Discord.EmbedAuthor":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Name":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Url":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.IconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ProxyIconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ToString":"Discord.EmbedAuthor.yml","Discord.EmbedBuilder":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.#ctor":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Title":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Description":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Url":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ThumbnailUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ImageUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Timestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Color":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Author":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Footer":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTitle(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithDescription(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithThumbnailUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithImageUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithCurrentTimestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTimestamp(System.DateTimeOffset)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithColor(Discord.Color)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(Discord.EmbedAuthorBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(System.Action{Discord.EmbedAuthorBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(Discord.EmbedFooterBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(System.Action{Discord.EmbedFooterBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.AddField(System.Action{Discord.EmbedFieldBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedFieldBuilder":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Name":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Value":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.IsInline":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.#ctor":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithName(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithValue(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithIsInline(System.Boolean)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedAuthorBuilder":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Name":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Url":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.IconUrl":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.#ctor":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithName(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithIconUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedFooterBuilder":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.Text":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.IconUrl":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.#ctor":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithText(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithIconUrl(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedField":"Discord.EmbedField.yml","Discord.EmbedField.Name":"Discord.EmbedField.yml","Discord.EmbedField.Value":"Discord.EmbedField.yml","Discord.EmbedField.Inline":"Discord.EmbedField.yml","Discord.EmbedField.ToString":"Discord.EmbedField.yml","Discord.EmbedFooter":"Discord.EmbedFooter.yml","Discord.EmbedFooter.Text":"Discord.EmbedFooter.yml","Discord.EmbedFooter.IconUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ProxyUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ToString":"Discord.EmbedFooter.yml","Discord.EmbedImage":"Discord.EmbedImage.yml","Discord.EmbedImage.Url":"Discord.EmbedImage.yml","Discord.EmbedImage.ProxyUrl":"Discord.EmbedImage.yml","Discord.EmbedImage.Height":"Discord.EmbedImage.yml","Discord.EmbedImage.Width":"Discord.EmbedImage.yml","Discord.EmbedImage.ToString":"Discord.EmbedImage.yml","Discord.EmbedProvider":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Name":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Url":"Discord.EmbedProvider.yml","Discord.EmbedProvider.ToString":"Discord.EmbedProvider.yml","Discord.EmbedThumbnail":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Url":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ProxyUrl":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Height":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Width":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ToString":"Discord.EmbedThumbnail.yml","Discord.EmbedVideo":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Url":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Height":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Width":"Discord.EmbedVideo.yml","Discord.EmbedVideo.ToString":"Discord.EmbedVideo.yml","Discord.Emoji":"Discord.Emoji.yml","Discord.Emoji.Id":"Discord.Emoji.yml","Discord.Emoji.Name":"Discord.Emoji.yml","Discord.Emoji.Url":"Discord.Emoji.yml","Discord.Emoji.Parse(System.String)":"Discord.Emoji.yml","Discord.Emoji.TryParse(System.String,Discord.Emoji@)":"Discord.Emoji.yml","Discord.Emoji.ToString":"Discord.Emoji.yml","Discord.IAttachment":"Discord.IAttachment.yml","Discord.IAttachment.Id":"Discord.IAttachment.yml","Discord.IAttachment.Filename":"Discord.IAttachment.yml","Discord.IAttachment.Url":"Discord.IAttachment.yml","Discord.IAttachment.ProxyUrl":"Discord.IAttachment.yml","Discord.IAttachment.Size":"Discord.IAttachment.yml","Discord.IAttachment.Height":"Discord.IAttachment.yml","Discord.IAttachment.Width":"Discord.IAttachment.yml","Discord.IEmbed":"Discord.IEmbed.yml","Discord.IEmbed.Url":"Discord.IEmbed.yml","Discord.IEmbed.Type":"Discord.IEmbed.yml","Discord.IEmbed.Title":"Discord.IEmbed.yml","Discord.IEmbed.Description":"Discord.IEmbed.yml","Discord.IEmbed.Timestamp":"Discord.IEmbed.yml","Discord.IEmbed.Color":"Discord.IEmbed.yml","Discord.IEmbed.Image":"Discord.IEmbed.yml","Discord.IEmbed.Video":"Discord.IEmbed.yml","Discord.IEmbed.Author":"Discord.IEmbed.yml","Discord.IEmbed.Footer":"Discord.IEmbed.yml","Discord.IEmbed.Provider":"Discord.IEmbed.yml","Discord.IEmbed.Thumbnail":"Discord.IEmbed.yml","Discord.IEmbed.Fields":"Discord.IEmbed.yml","Discord.IMessage":"Discord.IMessage.yml","Discord.IMessage.Type":"Discord.IMessage.yml","Discord.IMessage.IsTTS":"Discord.IMessage.yml","Discord.IMessage.IsPinned":"Discord.IMessage.yml","Discord.IMessage.IsWebhook":"Discord.IMessage.yml","Discord.IMessage.Content":"Discord.IMessage.yml","Discord.IMessage.Timestamp":"Discord.IMessage.yml","Discord.IMessage.EditedTimestamp":"Discord.IMessage.yml","Discord.IMessage.Channel":"Discord.IMessage.yml","Discord.IMessage.Author":"Discord.IMessage.yml","Discord.IMessage.WebhookId":"Discord.IMessage.yml","Discord.IMessage.Attachments":"Discord.IMessage.yml","Discord.IMessage.Embeds":"Discord.IMessage.yml","Discord.IMessage.Tags":"Discord.IMessage.yml","Discord.IMessage.MentionedChannelIds":"Discord.IMessage.yml","Discord.IMessage.MentionedRoleIds":"Discord.IMessage.yml","Discord.IMessage.MentionedUserIds":"Discord.IMessage.yml","Discord.IReaction":"Discord.IReaction.yml","Discord.IReaction.Emoji":"Discord.IReaction.yml","Discord.ISystemMessage":"Discord.ISystemMessage.yml","Discord.ITag":"Discord.ITag.yml","Discord.ITag.Index":"Discord.ITag.yml","Discord.ITag.Length":"Discord.ITag.yml","Discord.ITag.Type":"Discord.ITag.yml","Discord.ITag.Key":"Discord.ITag.yml","Discord.ITag.Value":"Discord.ITag.yml","Discord.IUserMessage":"Discord.IUserMessage.yml","Discord.IUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.PinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Reactions":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.IUserMessage.yml","Discord.MessageType":"Discord.MessageType.yml","Discord.MessageType.Default":"Discord.MessageType.yml","Discord.MessageType.RecipientAdd":"Discord.MessageType.yml","Discord.MessageType.RecipientRemove":"Discord.MessageType.yml","Discord.MessageType.Call":"Discord.MessageType.yml","Discord.MessageType.ChannelNameChange":"Discord.MessageType.yml","Discord.MessageType.ChannelIconChange":"Discord.MessageType.yml","Discord.MessageType.ChannelPinnedMessage":"Discord.MessageType.yml","Discord.ModifyMessageParams":"Discord.ModifyMessageParams.yml","Discord.ModifyMessageParams.Content":"Discord.ModifyMessageParams.yml","Discord.ModifyMessageParams.Embed":"Discord.ModifyMessageParams.yml","Discord.Tag`1":"Discord.Tag-1.yml","Discord.Tag`1.Type":"Discord.Tag-1.yml","Discord.Tag`1.Index":"Discord.Tag-1.yml","Discord.Tag`1.Length":"Discord.Tag-1.yml","Discord.Tag`1.Key":"Discord.Tag-1.yml","Discord.Tag`1.Value":"Discord.Tag-1.yml","Discord.Tag`1.ToString":"Discord.Tag-1.yml","Discord.Tag`1.Discord#ITag#Value":"Discord.Tag-1.yml","Discord.TagHandling":"Discord.TagHandling.yml","Discord.TagHandling.Ignore":"Discord.TagHandling.yml","Discord.TagHandling.Remove":"Discord.TagHandling.yml","Discord.TagHandling.Name":"Discord.TagHandling.yml","Discord.TagHandling.NameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.FullName":"Discord.TagHandling.yml","Discord.TagHandling.FullNameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.Sanitize":"Discord.TagHandling.yml","Discord.TagType":"Discord.TagType.yml","Discord.TagType.UserMention":"Discord.TagType.yml","Discord.TagType.ChannelMention":"Discord.TagType.yml","Discord.TagType.RoleMention":"Discord.TagType.yml","Discord.TagType.EveryoneMention":"Discord.TagType.yml","Discord.TagType.HereMention":"Discord.TagType.yml","Discord.TagType.Emoji":"Discord.TagType.yml","Discord.ChannelPermission":"Discord.ChannelPermission.yml","Discord.ChannelPermission.CreateInstantInvite":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageChannel":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AddReactions":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendTTSMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.EmbedLinks":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AttachFiles":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessageHistory":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MentionEveryone":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseExternalEmojis":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Connect":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Speak":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MuteMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.DeafenMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MoveMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseVAD":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManagePermissions":"Discord.ChannelPermission.yml","Discord.ChannelPermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.None":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.All(Discord.IChannel)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.RawValue":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.CreateInstantInvite":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageChannel":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AddReactions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendTTSMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.EmbedLinks":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AttachFiles":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessageHistory":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MentionEveryone":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseExternalEmojis":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Connect":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Speak":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MuteMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.DeafenMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MoveMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseVAD":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManagePermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.UInt64)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Has(Discord.ChannelPermission)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToList":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToString":"Discord.ChannelPermissions.yml","Discord.GuildPermission":"Discord.GuildPermission.yml","Discord.GuildPermission.CreateInstantInvite":"Discord.GuildPermission.yml","Discord.GuildPermission.KickMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.BanMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.Administrator":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageChannels":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageGuild":"Discord.GuildPermission.yml","Discord.GuildPermission.AddReactions":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendTTSMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.EmbedLinks":"Discord.GuildPermission.yml","Discord.GuildPermission.AttachFiles":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessageHistory":"Discord.GuildPermission.yml","Discord.GuildPermission.MentionEveryone":"Discord.GuildPermission.yml","Discord.GuildPermission.UseExternalEmojis":"Discord.GuildPermission.yml","Discord.GuildPermission.Connect":"Discord.GuildPermission.yml","Discord.GuildPermission.Speak":"Discord.GuildPermission.yml","Discord.GuildPermission.MuteMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.DeafenMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.MoveMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.UseVAD":"Discord.GuildPermission.yml","Discord.GuildPermission.ChangeNickname":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageNicknames":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageRoles":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageWebhooks":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageEmojis":"Discord.GuildPermission.yml","Discord.GuildPermissions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.None":"Discord.GuildPermissions.yml","Discord.GuildPermissions.All":"Discord.GuildPermissions.yml","Discord.GuildPermissions.RawValue":"Discord.GuildPermissions.yml","Discord.GuildPermissions.CreateInstantInvite":"Discord.GuildPermissions.yml","Discord.GuildPermissions.BanMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.KickMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Administrator":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageChannels":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageGuild":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AddReactions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendTTSMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.EmbedLinks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AttachFiles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessageHistory":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MentionEveryone":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseExternalEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Connect":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Speak":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MuteMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.DeafenMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MoveMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseVAD":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ChangeNickname":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageNicknames":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageRoles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageWebhooks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.UInt64)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Boolean,System.Boolean)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Has(Discord.GuildPermission)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToList":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToString":"Discord.GuildPermissions.yml","Discord.Overwrite":"Discord.Overwrite.yml","Discord.Overwrite.TargetId":"Discord.Overwrite.yml","Discord.Overwrite.TargetType":"Discord.Overwrite.yml","Discord.Overwrite.Permissions":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(System.UInt64,Discord.PermissionTarget,Discord.OverwritePermissions)":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(Discord.API.Overwrite)":"Discord.Overwrite.yml","Discord.OverwritePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.InheritAll":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.CreateInstantInvite":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageChannel":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AddReactions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendTTSMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.EmbedLinks":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AttachFiles":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessageHistory":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MentionEveryone":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseExternalEmojis":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Connect":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Speak":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MuteMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DeafenMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MoveMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseVAD":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManagePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(System.UInt64,System.UInt64)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Modify(System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue})":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToAllowList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToDenyList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToString":"Discord.OverwritePermissions.yml","Discord.PermissionTarget":"Discord.PermissionTarget.yml","Discord.PermissionTarget.Role":"Discord.PermissionTarget.yml","Discord.PermissionTarget.User":"Discord.PermissionTarget.yml","Discord.PermValue":"Discord.PermValue.yml","Discord.PermValue.Allow":"Discord.PermValue.yml","Discord.PermValue.Deny":"Discord.PermValue.yml","Discord.PermValue.Inherit":"Discord.PermValue.yml","Discord.Color":"Discord.Color.yml","Discord.Color.Default":"Discord.Color.yml","Discord.Color.RawValue":"Discord.Color.yml","Discord.Color.R":"Discord.Color.yml","Discord.Color.G":"Discord.Color.yml","Discord.Color.B":"Discord.Color.yml","Discord.Color.#ctor(System.UInt32)":"Discord.Color.yml","Discord.Color.#ctor(System.Byte,System.Byte,System.Byte)":"Discord.Color.yml","Discord.Color.#ctor(System.Single,System.Single,System.Single)":"Discord.Color.yml","Discord.Color.ToString":"Discord.Color.yml","Discord.IRole":"Discord.IRole.yml","Discord.IRole.Guild":"Discord.IRole.yml","Discord.IRole.Color":"Discord.IRole.yml","Discord.IRole.IsHoisted":"Discord.IRole.yml","Discord.IRole.IsManaged":"Discord.IRole.yml","Discord.IRole.IsMentionable":"Discord.IRole.yml","Discord.IRole.Name":"Discord.IRole.yml","Discord.IRole.Permissions":"Discord.IRole.yml","Discord.IRole.Position":"Discord.IRole.yml","Discord.IRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.IRole.yml","Discord.Game":"Discord.Game.yml","Discord.Game.Name":"Discord.Game.yml","Discord.Game.StreamUrl":"Discord.Game.yml","Discord.Game.StreamType":"Discord.Game.yml","Discord.Game.#ctor(System.String,System.String,Discord.StreamType)":"Discord.Game.yml","Discord.Game.ToString":"Discord.Game.yml","Discord.IConnection":"Discord.IConnection.yml","Discord.IConnection.Id":"Discord.IConnection.yml","Discord.IConnection.Type":"Discord.IConnection.yml","Discord.IConnection.Name":"Discord.IConnection.yml","Discord.IConnection.IsRevoked":"Discord.IConnection.yml","Discord.IConnection.IntegrationIds":"Discord.IConnection.yml","Discord.IGroupUser":"Discord.IGroupUser.yml","Discord.IGuildUser":"Discord.IGuildUser.yml","Discord.IGuildUser.JoinedAt":"Discord.IGuildUser.yml","Discord.IGuildUser.Nickname":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildPermissions":"Discord.IGuildUser.yml","Discord.IGuildUser.Guild":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildId":"Discord.IGuildUser.yml","Discord.IGuildUser.RoleIds":"Discord.IGuildUser.yml","Discord.IGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.IGuildUser.yml","Discord.IGuildUser.KickAsync(Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IPresence":"Discord.IPresence.yml","Discord.IPresence.Game":"Discord.IPresence.yml","Discord.IPresence.Status":"Discord.IPresence.yml","Discord.ISelfUser":"Discord.ISelfUser.yml","Discord.ISelfUser.Email":"Discord.ISelfUser.yml","Discord.ISelfUser.IsVerified":"Discord.ISelfUser.yml","Discord.ISelfUser.IsMfaEnabled":"Discord.ISelfUser.yml","Discord.ISelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.ISelfUser.yml","Discord.IUser":"Discord.IUser.yml","Discord.IUser.AvatarId":"Discord.IUser.yml","Discord.IUser.AvatarUrl":"Discord.IUser.yml","Discord.IUser.Discriminator":"Discord.IUser.yml","Discord.IUser.DiscriminatorValue":"Discord.IUser.yml","Discord.IUser.IsBot":"Discord.IUser.yml","Discord.IUser.Username":"Discord.IUser.yml","Discord.IUser.GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IUser.yml","Discord.IUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.IUser.yml","Discord.IVoiceState":"Discord.IVoiceState.yml","Discord.IVoiceState.IsDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSuppressed":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceChannel":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceSessionId":"Discord.IVoiceState.yml","Discord.StreamType":"Discord.StreamType.yml","Discord.StreamType.NotStreaming":"Discord.StreamType.yml","Discord.StreamType.Twitch":"Discord.StreamType.yml","Discord.UserStatus":"Discord.UserStatus.yml","Discord.UserStatus.Unknown":"Discord.UserStatus.yml","Discord.UserStatus.Online":"Discord.UserStatus.yml","Discord.UserStatus.Idle":"Discord.UserStatus.yml","Discord.UserStatus.AFK":"Discord.UserStatus.yml","Discord.UserStatus.DoNotDisturb":"Discord.UserStatus.yml","Discord.UserStatus.Invisible":"Discord.UserStatus.yml","Discord.UserStatus.Offline":"Discord.UserStatus.yml","Discord.AsyncEnumerableExtensions":"Discord.AsyncEnumerableExtensions.yml","Discord.AsyncEnumerableExtensions.Flatten``1(System.Collections.Generic.IAsyncEnumerable{System.Collections.Generic.IReadOnlyCollection{``0}})":"Discord.AsyncEnumerableExtensions.yml","Discord.DiscordClientExtensions":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetPrivateChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetOptimalVoiceRegionAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.GuildExtensions":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetAFKChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetDefaultChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetEmbedChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetOwnerAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildUserExtensions":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.ChangeRolesAsync(Discord.IGuildUser,System.Collections.Generic.IEnumerable{Discord.IRole},System.Collections.Generic.IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.LogMessage":"Discord.LogMessage.yml","Discord.LogMessage.Severity":"Discord.LogMessage.yml","Discord.LogMessage.Source":"Discord.LogMessage.yml","Discord.LogMessage.Message":"Discord.LogMessage.yml","Discord.LogMessage.Exception":"Discord.LogMessage.yml","Discord.LogMessage.#ctor(Discord.LogSeverity,System.String,System.String,System.Exception)":"Discord.LogMessage.yml","Discord.LogMessage.ToString":"Discord.LogMessage.yml","Discord.LogMessage.ToString(System.Text.StringBuilder,System.Boolean,System.Boolean,System.DateTimeKind,System.Nullable{System.Int32})":"Discord.LogMessage.yml","Discord.LogSeverity":"Discord.LogSeverity.yml","Discord.LogSeverity.Critical":"Discord.LogSeverity.yml","Discord.LogSeverity.Error":"Discord.LogSeverity.yml","Discord.LogSeverity.Warning":"Discord.LogSeverity.yml","Discord.LogSeverity.Info":"Discord.LogSeverity.yml","Discord.LogSeverity.Verbose":"Discord.LogSeverity.yml","Discord.LogSeverity.Debug":"Discord.LogSeverity.yml","Discord.RpcException":"Discord.RpcException.yml","Discord.RpcException.ErrorCode":"Discord.RpcException.yml","Discord.RpcException.Reason":"Discord.RpcException.yml","Discord.RpcException.#ctor(System.Int32,System.String)":"Discord.RpcException.yml","Discord.MentionUtils":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionUser(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionChannel(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionRole(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseUser(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseUser(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseChannel(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseChannel(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseRole(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseRole(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.Optional`1":"Discord.Optional-1.yml","Discord.Optional`1.Unspecified":"Discord.Optional-1.yml","Discord.Optional`1.Value":"Discord.Optional-1.yml","Discord.Optional`1.IsSpecified":"Discord.Optional-1.yml","Discord.Optional`1.#ctor(`0)":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault(`0)":"Discord.Optional-1.yml","Discord.Optional`1.Equals(System.Object)":"Discord.Optional-1.yml","Discord.Optional`1.GetHashCode":"Discord.Optional-1.yml","Discord.Optional`1.ToString":"Discord.Optional-1.yml","Discord.Optional`1.op_Implicit(`0)~Discord.Optional{`0}":"Discord.Optional-1.yml","Discord.Optional`1.op_Explicit(Discord.Optional{`0})~`0":"Discord.Optional-1.yml","Discord.Optional":"Discord.Optional.yml","Discord.Optional.Create``1":"Discord.Optional.yml","Discord.Optional.Create``1(``0)":"Discord.Optional.yml","Discord.RestGuildEmbed":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.IsEnabled":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ChannelId":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ToString":"Discord.RestGuildEmbed.yml","Discord.RestVoiceRegion":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.Name":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsVip":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsOptimal":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SampleHostname":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SamplePort":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.ToString":"Discord.RestVoiceRegion.yml","Discord.Attachment":"Discord.Attachment.yml","Discord.Attachment.Id":"Discord.Attachment.yml","Discord.Attachment.Filename":"Discord.Attachment.yml","Discord.Attachment.Url":"Discord.Attachment.yml","Discord.Attachment.ProxyUrl":"Discord.Attachment.yml","Discord.Attachment.Size":"Discord.Attachment.yml","Discord.Attachment.Height":"Discord.Attachment.yml","Discord.Attachment.Width":"Discord.Attachment.yml","Discord.Attachment.ToString":"Discord.Attachment.yml","Discord.Embed":"Discord.Embed.yml","Discord.Embed.Description":"Discord.Embed.yml","Discord.Embed.Url":"Discord.Embed.yml","Discord.Embed.Title":"Discord.Embed.yml","Discord.Embed.Type":"Discord.Embed.yml","Discord.Embed.Timestamp":"Discord.Embed.yml","Discord.Embed.Color":"Discord.Embed.yml","Discord.Embed.Image":"Discord.Embed.yml","Discord.Embed.Video":"Discord.Embed.yml","Discord.Embed.Author":"Discord.Embed.yml","Discord.Embed.Footer":"Discord.Embed.yml","Discord.Embed.Provider":"Discord.Embed.yml","Discord.Embed.Thumbnail":"Discord.Embed.yml","Discord.Embed.Fields":"Discord.Embed.yml","Discord.Embed.ToString":"Discord.Embed.yml","Discord.RestReaction":"Discord.RestReaction.yml","Discord.RestReaction.Emoji":"Discord.RestReaction.yml","Discord.RestReaction.Count":"Discord.RestReaction.yml","Discord.RestReaction.Me":"Discord.RestReaction.yml","Discord.RestConnection":"Discord.RestConnection.yml","Discord.RestConnection.Id":"Discord.RestConnection.yml","Discord.RestConnection.Type":"Discord.RestConnection.yml","Discord.RestConnection.Name":"Discord.RestConnection.yml","Discord.RestConnection.IsRevoked":"Discord.RestConnection.yml","Discord.RestConnection.IntegrationIds":"Discord.RestConnection.yml","Discord.RestConnection.ToString":"Discord.RestConnection.yml","Discord.Audio":"Discord.Audio.yml","Discord.Audio.IAudioClient":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Connected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Disconnected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.LatencyUpdated":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.ConnectionState":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Latency":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.DisconnectAsync":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreatePCMStream(System.Int32,System.Nullable{System.Int32},Discord.Audio.OpusApplication,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.OpusApplication":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.Voice":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.MusicOrMixed":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.LowLatency":"Discord.Audio.OpusApplication.yml","Discord.Audio.DiscordVoiceAPIClient":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.MaxBitrate":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Mode":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentRequest":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentGatewayMessage":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentDiscovery":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SentData":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedEvent":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ReceivedPacket":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Disconnected":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.GuildId":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectionState":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.Dispose":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(Discord.API.Voice.VoiceOpCode,System.Object,Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendAsync(System.Byte[],System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendHeartbeatAsync(Discord.RequestOptions)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendIdentityAsync(System.UInt64,System.String,System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSelectProtocol(System.String,System.Int32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendSetSpeaking(System.Boolean)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.ConnectAsync(System.String)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.DisconnectAsync":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SendDiscoveryAsync(System.UInt32)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.DiscordVoiceAPIClient.SetUdpEndpoint(System.Net.IPEndPoint)":"Discord.Audio.DiscordVoiceAPIClient.yml","Discord.Audio.AudioClient":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Connected":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Disconnected":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.LatencyUpdated":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Guild":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.ApiClient":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.ConnectionState":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Latency":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.DisconnectAsync":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Send(System.Byte[],System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.CreatePCMStream(System.Int32,System.Nullable{System.Int32},Discord.Audio.OpusApplication,System.Int32)":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioClient.Dispose":"Discord.Audio.AudioClient.yml","Discord.Audio.AudioMode":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Disabled":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Outgoing":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Incoming":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Both":"Discord.Audio.AudioMode.yml","Discord.Audio.SecretBox":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Encrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Decrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Rest":"Discord.Rest.yml","Discord.Rest.BaseDiscordClient":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Log":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedIn":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedOut":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.ApiClient":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginState":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.CurrentUser":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginAsync(Discord.TokenType,System.String,System.Boolean)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Dispose":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#ConnectionState":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#CurrentUser":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetUserAsync(System.String,System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#ConnectAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Discord#IDiscordClient#DisconnectAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.DiscordRestClient":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CurrentUser":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor(Discord.Rest.DiscordRestConfig)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLogoutAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetChannelAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetPrivateChannelsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildEmbedAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildSummariesAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetUserAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildUserAsync(System.UInt64,System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestConfig":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.UserAgent":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.RestClientProvider":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.RestApplication":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication._iconId":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Name":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Description":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.RPCOrigins":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Flags":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Owner":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.CreatedAt":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.IconUrl":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.UpdateAsync":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.ToString":"Discord.Rest.RestApplication.yml","Discord.Rest.RestEntity`1":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Discord":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Id":"Discord.Rest.RestEntity-1.yml","Discord.Rest.IRestAudioChannel":"Discord.Rest.IRestAudioChannel.yml","Discord.Rest.IRestMessageChannel":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestPrivateChannel":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.IRestPrivateChannel.Recipients":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.RestChannel":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.CreatedAt":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#Name":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestDMChannel":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CurrentUser":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Recipient":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Users":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetUser(System.UInt64)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.ToString":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IDMChannel#Recipient":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IPrivateChannel#Recipients":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#Name":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestGroupChannel":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Name":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Users":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetUser(System.UInt64)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.ToString":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGuildChannel":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.PermissionOverwrites":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Name":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Position":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GuildId":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ToString":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#Guild":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestTextChannel":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Topic":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Mention":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUserAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUsersAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestVoiceChannel":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Bitrate":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.UserLimit":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestBan":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.User":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.Reason":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.ToString":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.Discord#IBan#User":"Discord.Rest.RestBan.yml","Discord.Rest.RestGuild":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Name":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKTimeout":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IsEmbeddable":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VerificationLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.MfaLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultMessageNotifications":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EmbedChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.OwnerId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VoiceRegionId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreatedAt":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EveryoneRole":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Roles":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Emojis":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Features":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetBansAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetRole(System.UInt64)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUsersAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUserAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetCurrentUserAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ToString":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#Available":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#AudioClient":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#EveryoneRole":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#Roles":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetBansAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetIntegrationsAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetRole(System.UInt64)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Discord#IGuild#DownloadUsersAsync":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuildIntegration":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Name":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Type":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsEnabled":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsSyncing":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireBehavior":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireGracePeriod":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.GuildId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.RoleId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.User":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Account":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncedAt":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Update(Discord.API.Integration)":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.DeleteAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildIntegrationParams})":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ToString":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Discord#IGuildIntegration#Guild":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Discord#IGuildIntegration#User":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestUserGuild":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Name":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IsOwner":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Permissions":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.CreatedAt":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IconUrl":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Update(Discord.API.UserGuild)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.LeaveAsync(Discord.RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.ToString":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestInvite":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Code":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Url":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.AcceptAsync(Discord.RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ToString":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Discord#IInvite#Guild":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Discord#IInvite#Channel":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInviteMetadata":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsRevoked":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsTemporary":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxAge":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxUses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Uses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Inviter":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.CreatedAt":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Discord#IInviteMetadata#Inviter":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestMessage":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Channel":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Author":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Content":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.CreatedAt":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsTTS":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsPinned":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.EditedTimestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Attachments":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Embeds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedChannelIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedRoleIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedUsers":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Tags":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.WebhookId":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsWebhook":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Timestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.ToString":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Type":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Author":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Attachments":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#Embeds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Discord#IMessage#MentionedUserIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestSystemMessage":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestSystemMessage.Type":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestUserMessage":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsTTS":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsPinned":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.WebhookId":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.EditedTimestamp":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Attachments":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Embeds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedChannelIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedRoleIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedUsers":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Tags":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Reactions":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.PinAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestRole":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Color":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsHoisted":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsManaged":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsMentionable":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Name":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Permissions":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Position":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CreatedAt":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsEveryone":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Mention":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.DeleteAsync(Discord.RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CompareTo(Discord.IRole)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ToString":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Discord#IRole#Guild":"Discord.Rest.RestRole.yml","Discord.Rest.RestGroupUser":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsDeafened":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsMuted":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSelfDeafened":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSelfMuted":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#IsSuppressed":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#VoiceChannel":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGroupUser.Discord#IVoiceState#VoiceSessionId":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGuildUser":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Nickname":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsDeafened":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsMuted":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildId":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildPermissions":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.RoleIds":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.JoinedAt":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.KickAsync(Discord.RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IGuildUser#Guild":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSelfDeafened":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSelfMuted":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#IsSuppressed":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#VoiceChannel":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Discord#IVoiceState#VoiceSessionId":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestSelfUser":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.Email":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsVerified":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsMfaEnabled":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestUser":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.IsBot":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Username":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.DiscriminatorValue":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarId":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarUrl":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreatedAt":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discriminator":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Mention":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Game":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Status":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.UpdateAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.ToString":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rpc":"Discord.Rpc.yml","Discord.Rpc.DiscordRpcClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectionState":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Scopes":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.TokenExpiresAt":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApiClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.CurrentUser":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApplicationInfo":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String,Discord.Rpc.DiscordRpcConfig)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.DisconnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.AuthorizeAsync(System.String[],System.String,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGlobal(Discord.Rpc.RpcGlobalEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGlobal(Discord.Rpc.RpcGlobalEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildsAsync(Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelsAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(Discord.IChannel,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(Discord.Rpc.RpcChannelSummary,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(Discord.IChannel,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(Discord.Rpc.RpcChannelSummary,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(System.UInt64,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetVoiceSettingsAsync(Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetVoiceSettingsAsync(System.Action{Discord.API.Rpc.VoiceSettings},Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetUserVoiceSettingsAsync(System.UInt64,System.Action{Discord.API.Rpc.UserVoiceSettings},Discord.RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Connected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Disconnected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Ready":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ChannelCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildStatusUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStarted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStopped":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceSettingsUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageReceived":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcConfig":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.RpcAPIVersion":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeStart":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeEnd":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.ConnectionTimeout":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.WebSocketProvider":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.RpcChannelEvent":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStart":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStop":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcGlobalEvent":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.ChannelCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.GuildCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.VoiceSettingsUpdated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGuildEvent":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcGuildEvent.GuildStatus":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcEntity`1":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Discord":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Id":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.VoiceDevice":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Id":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Name":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.ToString":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceSettings":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableInputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableOutputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutomaticGainControl":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.EchoCancellation":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.NoiseSuppression":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.QualityOfService":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.SilenceWarning":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.ActivationMode":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutoThreshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Threshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Shortcuts":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Delay":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceShortcut":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Type":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Code":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Name":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.ToString":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcutType":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.MouseButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardModifierKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.GamepadButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.IRpcAudioChannel":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcAudioChannel.VoiceStates":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcMessageChannel":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcMessageChannel.CachedMessages":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcPrivateChannel":"Discord.Rpc.IRpcPrivateChannel.yml","Discord.Rpc.RpcChannel":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.Name":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.CreatedAt":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannelSummary":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Id":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Name":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Type":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.ToString":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcDMChannel":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CachedMessages":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.ToString":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IDMChannel#Recipient":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IPrivateChannel#Recipients":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#Name":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcGroupChannel":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.CachedMessages":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.VoiceStates":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.ToString":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#Name":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGuildChannel":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GuildId":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Position":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ToString":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#Guild":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#PermissionOverwrites":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcTextChannel":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.CachedMessages":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Mention":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#ITextChannel#Topic":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcVoiceChannel":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.UserLimit":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.Bitrate":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.VoiceStates":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcGuild":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Name":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.IconUrl":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Users":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.ToString":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuildStatus":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Guild":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Online":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.ToString":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildSummary":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Id":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Name":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.ToString":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcMessage":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Channel":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Author":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Content":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.AuthorColor":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.CreatedAt":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsTTS":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsPinned":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsBlocked":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.EditedTimestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Attachments":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Embeds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedChannelIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedRoleIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedUserIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Tags":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.WebhookId":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsWebhook":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Timestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.DeleteAsync(Discord.RequestOptions)":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.ToString":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Type":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Author":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Attachments":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Discord#IMessage#Embeds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcSystemMessage":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcSystemMessage.Type":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcUserMessage":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsTTS":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsPinned":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsBlocked":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.WebhookId":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.EditedTimestamp":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Attachments":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Embeds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedChannelIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedRoleIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedUserIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Tags":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Reactions":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.PinAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.Pan":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Left":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Right":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.#ctor(System.Single,System.Single)":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.ToString":"Discord.Rpc.Pan.yml","Discord.Rpc.RpcGuildUser":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcGuildUser.Status":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcUser":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.IsBot":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Username":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.DiscriminatorValue":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarId":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarUrl":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreatedAt":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discriminator":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Mention":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Game":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Status":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.ToString":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcVoiceState":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.User":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Nickname":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Volume":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted2":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Pan":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSuppressed":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.ToString":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Discord#IVoiceState#VoiceSessionId":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Discord#IVoiceState#VoiceChannel":"Discord.Rpc.RpcVoiceState.yml","Discord.WebSocket":"Discord.WebSocket.yml","Discord.WebSocket.DiscordSocketClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ShardId":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectionState":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Latency":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ApiClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUser":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Guilds":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.PrivateChannels":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor(Discord.WebSocket.DiscordSocketConfig)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLoginAsync(Discord.TokenType,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLogoutAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectAsync(System.Boolean)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DisconnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetApplicationInfoAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuild(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetChannel(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetConnectionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetInviteAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetVoiceRegion(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadAllUsersAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(System.Collections.Generic.IEnumerable{Discord.IGuild})":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(Discord.IGuild[])":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetStatus(Discord.UserStatus)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetGame(System.String,System.String,Discord.StreamType)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#ApiClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#ConnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetApplicationInfoAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetConnectionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetInviteAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetGuildsAsync(Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#CreateGuildAsync(System.String,Discord.IVoiceRegion,System.IO.Stream)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetUserAsync(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Discord#IDiscordClient#GetVoiceRegionAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Connected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Disconnected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Ready":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LatencyUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelDestroyed":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageReceived":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionsCleared":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.JoinedGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LeftGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildAvailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUnavailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMembersDownloaded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserJoined":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserLeft":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserBanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUnbanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMemberUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserPresenceUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserVoiceStateUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserIsTyping":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketConfig":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.GatewayEncoding":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ConnectionTimeout":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ShardId":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.TotalShards":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.MessageCacheSize":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.LargeThreshold":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.AudioMode":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.WebSocketProvider":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.SocketEntity`1":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Discord":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Id":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.ISocketAudioChannel":"Discord.WebSocket.ISocketAudioChannel.yml","Discord.WebSocket.ISocketMessageChannel":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.CachedMessages":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketPrivateChannel":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.ISocketPrivateChannel.Recipients":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.SocketChannel":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.CreatedAt":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Users":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#Name":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketDMChannel":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Recipient":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CachedMessages":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Users":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.ToString":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IDMChannel#Recipient":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IPrivateChannel#Recipients":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#Name":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketGroupChannel":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Name":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.CachedMessages":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Users":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.ToString":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IPrivateChannel#Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGuildChannel":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Guild":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Name":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Position":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.PermissionOverwrites":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Users":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ToString":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#Guild":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GuildId":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IRole)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetPermissionOverwrite(Discord.IUser)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Discord#IChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketTextChannel":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Topic":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Mention":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.CachedMessages":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Users":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyTextChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessageAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.UInt64,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(Discord.IMessage,Discord.Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.DeleteMessagesAsync(System.Collections.Generic.IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendFileAsync(System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendFileAsync(System.IO.Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#SendMessageAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Discord#IMessageChannel#EnterTypingState(Discord.RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketVoiceChannel":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Bitrate":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.UserLimit":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Users":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.ModifyAsync(System.Action{Discord.API.Rest.ModifyVoiceChannelParams},Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IVoiceChannel#ConnectAsync":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IGuildChannel#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Discord#IGuildChannel#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketGuild":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Name":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKTimeout":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsEmbeddable":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VerificationLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MfaLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultMessageNotifications":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadedMemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AudioClient":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EmbedChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.OwnerId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VoiceRegionId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreatedAt":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.HasAllMembers":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsSynced":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SyncPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloaderPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CurrentUser":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EveryoneRole":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Channels":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Emojis":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Features":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Users":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Roles":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VoiceStates":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyEmbedAsync(System.Action{Discord.API.Rest.ModifyGuildEmbedParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyChannelsAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildChannelsParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyRolesAsync(System.Collections.Generic.IEnumerable{Discord.API.Rest.ModifyGuildRolesParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.LeaveAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetBansAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetChannel(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetRole(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadUsersAsync":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DisconnectAudioAsync(Discord.Audio.AudioClient)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ToString":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#Available":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#AudioClient":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#EveryoneRole":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#Roles":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetBansAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetIntegrationsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetInvitesAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetRole(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Discord#IGuild#DownloadUsersAsync":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketMessage":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Author":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Channel":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Content":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.CreatedAt":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsTTS":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsPinned":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.EditedTimestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Attachments":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Embeds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedChannels":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedRoles":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedUsers":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Tags":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.WebhookId":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsWebhook":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Timestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.ToString":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Author":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Channel":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Type":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Attachments":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#Embeds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedChannelIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedRoleIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Discord#IMessage#MentionedUserIds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketReaction":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.UserId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.User":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.MessageId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Message":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Channel":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Emoji":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketUserMessage":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsTTS":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsPinned":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.WebhookId":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.EditedTimestamp":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Attachments":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Embeds":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Tags":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedChannels":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedRoles":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedUsers":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Reactions":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.ModifyAsync(System.Action{Discord.ModifyMessageParams},Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.PinAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(System.Int32,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketRole":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Guild":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Color":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsHoisted":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsManaged":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsMentionable":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Name":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Permissions":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Position":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CreatedAt":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsEveryone":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Mention":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildRoleParams},Discord.RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.DeleteAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ToString":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Discord#IRole#Guild":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CompareTo(Discord.IRole)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketGroupUser":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Channel":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.IsBot":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Username":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.DiscriminatorValue":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.AvatarId":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsDeafened":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsMuted":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSelfDeafened":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSelfMuted":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#IsSuppressed":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Discord#IVoiceState#VoiceSessionId":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGuildUser":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Guild":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Nickname":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsBot":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Username":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.DiscriminatorValue":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.AvatarId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GuildPermissions":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSuppressed":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.JoinedAt":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.RoleIds":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceChannel":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceSessionId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceState":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Hierarchy":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyGuildMemberParams},Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.KickAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#Guild":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#GuildId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IGuildUser#RoleIds":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketPresence":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Status":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Game":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.ToString":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketSelfUser":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Email":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsVerified":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsMfaEnabled":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsBot":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Username":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.DiscriminatorValue":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.AvatarId":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.ModifyAsync(System.Action{Discord.API.Rest.ModifyCurrentUserParams},Discord.RequestOptions)":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSimpleUser":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.IsBot":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.Username":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.DiscriminatorValue":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.AvatarId":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketUser":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.IsBot":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Username":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.DiscriminatorValue":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarId":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarUrl":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreatedAt":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discriminator":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Mention":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Game":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Status":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.ToString":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discord#IUser#GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discord#IUser#CreateDMChannelAsync(Discord.RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketVoiceState":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceChannel":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceSessionId":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSuppressed":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.ToString":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.Discord#IVoiceState#VoiceChannel":"Discord.WebSocket.SocketVoiceState.yml","Discord.Commands":"Discord.Commands.yml","Discord.Commands.CommandContext":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Client":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Guild":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Channel":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.User":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Message":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.IsPrivate":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.#ctor(Discord.IDiscordClient,Discord.IGuild,Discord.IMessageChannel,Discord.IUser,Discord.IUserMessage)":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.#ctor(Discord.IDiscordClient,Discord.IUserMessage)":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandError":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnknownCommand":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ParseFailed":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.BadArgCount":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ObjectNotFound":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.MultipleMatches":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnmetPrecondition":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.Exception":"Discord.Commands.CommandError.yml","Discord.Commands.CommandService":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Modules":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Commands":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor(Discord.Commands.CommandServiceConfig)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.CreateModuleAsync(System.String,System.Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModulesAsync(System.Reflection.Assembly)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync(Discord.Commands.ModuleInfo)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader``1(Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader(System.Type,Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.Commands.CommandContext,System.Int32)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(Discord.Commands.CommandContext,System.String)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(Discord.Commands.CommandContext,System.Int32,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(Discord.Commands.CommandContext,System.String,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandServiceConfig":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.DefaultRunMode":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.CaseSensitiveCommands":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.ModuleBase":"Discord.Commands.ModuleBase.yml","Discord.Commands.ModuleBase.Context":"Discord.Commands.ModuleBase.yml","Discord.Commands.ModuleBase.ReplyAsync(System.String,System.Boolean,Discord.EmbedBuilder,Discord.RequestOptions)":"Discord.Commands.ModuleBase.yml","Discord.Commands.MultiMatchHandling":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Exception":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Best":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.RunMode":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Default":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Sync":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Mixed":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Async":"Discord.Commands.RunMode.yml","Discord.Commands.AliasAttribute":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.Aliases":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.#ctor(System.String[])":"Discord.Commands.AliasAttribute.yml","Discord.Commands.CommandAttribute":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.Text":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.RunMode":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor(System.String)":"Discord.Commands.CommandAttribute.yml","Discord.Commands.DontAutoLoadAttribute":"Discord.Commands.DontAutoLoadAttribute.yml","Discord.Commands.GroupAttribute":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.Prefix":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor(System.String)":"Discord.Commands.GroupAttribute.yml","Discord.Commands.NameAttribute":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.Text":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.#ctor(System.String)":"Discord.Commands.NameAttribute.yml","Discord.Commands.PreconditionAttribute":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PreconditionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PriorityAttribute":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.Priority":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.#ctor(System.Int32)":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.RemainderAttribute":"Discord.Commands.RemainderAttribute.yml","Discord.Commands.RemarksAttribute":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.Text":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.#ctor(System.String)":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.SummaryAttribute":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.Text":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.#ctor(System.String)":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.RequireBotPermissionAttribute":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.GuildPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.ChannelPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(Discord.GuildPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(Discord.ChannelPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.ContextType":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Guild":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.DM":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Group":"Discord.Commands.ContextType.yml","Discord.Commands.RequireContextAttribute":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.Contexts":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.#ctor(Discord.Commands.ContextType)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireOwnerAttribute":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireOwnerAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireUserPermissionAttribute":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.GuildPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.ChannelPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(Discord.GuildPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(Discord.ChannelPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.CheckPermissions(Discord.Commands.CommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.DependencyMap":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Empty":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.#ctor":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Add``1(``0)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get``1":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get(System.Type)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet``1(``0@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.IDependencyMap":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Add``1(``0)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get``1":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet``1(``0@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get(System.Type)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet(System.Type,System.Object@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IEnumerableExtensions":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.IEnumerableExtensions.Permutate``3(System.Collections.Generic.IEnumerable{``0},System.Collections.Generic.IEnumerable{``1},System.Func{``0,``1,``2})":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.MessageExtensions":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasCharPrefix(Discord.IUserMessage,System.Char,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasStringPrefix(Discord.IUserMessage,System.String,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasMentionPrefix(Discord.IUserMessage,Discord.IUser,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.CommandInfo":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Module":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Name":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Summary":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Remarks":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Priority":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.HasVarArgs":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.RunMode":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Aliases":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Parameters":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Preconditions":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.CheckPreconditionsAsync(Discord.Commands.CommandContext,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ParseAsync(Discord.Commands.CommandContext,Discord.Commands.SearchResult,System.Nullable{Discord.Commands.PreconditionResult})":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Execute(Discord.Commands.CommandContext,Discord.Commands.ParseResult,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ExecuteAsync(Discord.Commands.CommandContext,System.Collections.Generic.IEnumerable{System.Object},System.Collections.Generic.IEnumerable{System.Object},Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.ModuleInfo":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Service":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Name":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Summary":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Remarks":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Aliases":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Commands":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Preconditions":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Submodules":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ParameterInfo":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Command":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Name":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Summary":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsOptional":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsRemainder":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsMultiple":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Type":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.DefaultValue":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Parse(Discord.Commands.CommandContext,System.String)":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.ToString":"Discord.Commands.ParameterInfo.yml","Discord.Commands.TypeReader":"Discord.Commands.TypeReader.yml","Discord.Commands.TypeReader.Read(Discord.Commands.CommandContext,System.String)":"Discord.Commands.TypeReader.yml","Discord.Commands.ExecuteResult":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Exception":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Error":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ErrorReason":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.IsSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(System.Exception)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ToString":"Discord.Commands.ExecuteResult.yml","Discord.Commands.IResult":"Discord.Commands.IResult.yml","Discord.Commands.IResult.Error":"Discord.Commands.IResult.yml","Discord.Commands.IResult.ErrorReason":"Discord.Commands.IResult.yml","Discord.Commands.IResult.IsSuccess":"Discord.Commands.IResult.yml","Discord.Commands.ParseResult":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ArgValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ParamValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.Error":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ErrorReason":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.IsSuccess":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderResult},System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderResult})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderValue},System.Collections.Generic.IReadOnlyList{Discord.Commands.TypeReaderValue})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ToString":"Discord.Commands.ParseResult.yml","Discord.Commands.PreconditionResult":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.Error":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ErrorReason":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.IsSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(System.String)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(Discord.Commands.IResult)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ToString":"Discord.Commands.PreconditionResult.yml","Discord.Commands.SearchResult":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Text":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Commands":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Error":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ErrorReason":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.IsSuccess":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromSuccess(System.String,System.Collections.Generic.IReadOnlyList{Discord.Commands.CommandInfo})":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.IResult)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ToString":"Discord.Commands.SearchResult.yml","Discord.Commands.TypeReaderValue":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Value":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Score":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.#ctor(System.Object,System.Single)":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.ToString":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderResult":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Values":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Error":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ErrorReason":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.IsSuccess":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Object)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(Discord.Commands.TypeReaderValue)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Collections.Generic.IReadOnlyCollection{Discord.Commands.TypeReaderValue})":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.IResult)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ToString":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.Builders":"Discord.Commands.Builders.yml","Discord.Commands.Builders.CommandBuilder":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Module":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Name":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Summary":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Remarks":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.RunMode":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Priority":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Preconditions":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Parameters":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Aliases":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithName(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithSummary(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRunMode(Discord.Commands.RunMode)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithPriority(System.Int32)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddAliases(System.String[])":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddParameter(System.String,System.Type,System.Action{Discord.Commands.Builders.ParameterBuilder})":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.ModuleBuilder":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Service":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Parent":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Name":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Summary":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Remarks":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Commands":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Modules":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Preconditions":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Aliases":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithName(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddAlias(System.String[])":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddCommand(System.String,System.Func{Discord.Commands.CommandContext,System.Object[],Discord.Commands.IDependencyMap,System.Threading.Tasks.Task},System.Action{Discord.Commands.Builders.CommandBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddModule(System.String,System.Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Build(Discord.Commands.CommandService)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ParameterBuilder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Command":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Name":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.ParameterType":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.TypeReader":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsOptional":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsRemainder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsMultiple":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.DefaultValue":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Summary":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithDefault(System.Object)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsOptional(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsRemainder(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsMultiple(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml"} \ No newline at end of file +{"Discord.Rpc":"Discord.Rpc.yml","Discord.Rpc.DiscordRpcClient":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectionState":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Scopes":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.TokenExpiresAt":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.CurrentUser":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ApplicationInfo":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.#ctor(System.String,System.String,Discord.Rpc.DiscordRpcConfig)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ConnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.DisconnectAsync":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.AuthorizeAsync(System.String[],System.String,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGlobal(Discord.Rpc.RpcGlobalEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGlobal(Discord.Rpc.RpcGlobalEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeGuild(System.UInt64,Discord.Rpc.RpcChannelEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.UnsubscribeChannel(System.UInt64,Discord.Rpc.RpcChannelEvent,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildAsync(System.UInt64,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcGuildsAsync(RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelAsync(System.UInt64,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetRpcChannelsAsync(System.UInt64,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(IChannel,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(Discord.Rpc.RpcChannelSummary,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectTextChannelAsync(System.UInt64,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(IChannel,System.Boolean,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(Discord.Rpc.RpcChannelSummary,System.Boolean,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SelectVoiceChannelAsync(System.UInt64,System.Boolean,RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GetVoiceSettingsAsync(RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetVoiceSettingsAsync(Action{Discord.Rpc.VoiceProperties},RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SetUserVoiceSettingsAsync(System.UInt64,Action{Discord.Rpc.UserVoiceProperties},RequestOptions)":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Connected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Disconnected":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.Ready":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.ChannelCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.GuildStatusUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateCreated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceStateDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStarted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.SpeakingStopped":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.VoiceSettingsUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageReceived":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageUpdated":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcClient.MessageDeleted":"Discord.Rpc.DiscordRpcClient.yml","Discord.Rpc.DiscordRpcConfig":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.RpcAPIVersion":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeStart":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.PortRangeEnd":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.ConnectionTimeout":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.WebSocketProvider":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.DiscordRpcConfig.#ctor":"Discord.Rpc.DiscordRpcConfig.yml","Discord.Rpc.RpcChannelEvent":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.VoiceStateDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStart":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.SpeakingStop":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageCreate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageUpdate":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcChannelEvent.MessageDelete":"Discord.Rpc.RpcChannelEvent.yml","Discord.Rpc.RpcGlobalEvent":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.ChannelCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.GuildCreated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGlobalEvent.VoiceSettingsUpdated":"Discord.Rpc.RpcGlobalEvent.yml","Discord.Rpc.RpcGuildEvent":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcGuildEvent.GuildStatus":"Discord.Rpc.RpcGuildEvent.yml","Discord.Rpc.RpcEntity`1":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Discord":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.RpcEntity`1.Id":"Discord.Rpc.RpcEntity-1.yml","Discord.Rpc.UserVoiceProperties":"Discord.Rpc.UserVoiceProperties.yml","Discord.Rpc.UserVoiceProperties.Pan":"Discord.Rpc.UserVoiceProperties.yml","Discord.Rpc.UserVoiceProperties.Volume":"Discord.Rpc.UserVoiceProperties.yml","Discord.Rpc.UserVoiceProperties.Mute":"Discord.Rpc.UserVoiceProperties.yml","Discord.Rpc.VoiceDevice":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Id":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.Name":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDevice.ToString":"Discord.Rpc.VoiceDevice.yml","Discord.Rpc.VoiceDeviceProperties":"Discord.Rpc.VoiceDeviceProperties.yml","Discord.Rpc.VoiceDeviceProperties.DeviceId":"Discord.Rpc.VoiceDeviceProperties.yml","Discord.Rpc.VoiceDeviceProperties.Volume":"Discord.Rpc.VoiceDeviceProperties.yml","Discord.Rpc.VoiceDeviceProperties.AvailableDevices":"Discord.Rpc.VoiceDeviceProperties.yml","Discord.Rpc.VoiceModeProperties":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceModeProperties.Type":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceModeProperties.AutoThreshold":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceModeProperties.Threshold":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceModeProperties.Shortcut":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceModeProperties.Delay":"Discord.Rpc.VoiceModeProperties.yml","Discord.Rpc.VoiceProperties":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.Input":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.Output":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.Mode":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.AutomaticGainControl":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.EchoCancellation":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.NoiseSuppression":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.QualityOfService":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceProperties.SilenceWarning":"Discord.Rpc.VoiceProperties.yml","Discord.Rpc.VoiceSettings":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.InputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableInputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputDeviceId":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.OutputVolume":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AvailableOutputDevices":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutomaticGainControl":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.EchoCancellation":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.NoiseSuppression":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.QualityOfService":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.SilenceWarning":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.ActivationMode":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.AutoThreshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Threshold":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Shortcuts":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceSettings.Delay":"Discord.Rpc.VoiceSettings.yml","Discord.Rpc.VoiceShortcut":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Type":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Code":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.Name":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcut.ToString":"Discord.Rpc.VoiceShortcut.yml","Discord.Rpc.VoiceShortcutType":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.MouseButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.KeyboardModifierKey":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.VoiceShortcutType.GamepadButton":"Discord.Rpc.VoiceShortcutType.yml","Discord.Rpc.IRpcAudioChannel":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcAudioChannel.VoiceStates":"Discord.Rpc.IRpcAudioChannel.yml","Discord.Rpc.IRpcMessageChannel":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcMessageChannel.CachedMessages":"Discord.Rpc.IRpcMessageChannel.yml","Discord.Rpc.IRpcPrivateChannel":"Discord.Rpc.IRpcPrivateChannel.yml","Discord.Rpc.RpcChannel":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.Name":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannel.CreatedAt":"Discord.Rpc.RpcChannel.yml","Discord.Rpc.RpcChannelSummary":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Id":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Name":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.Type":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcChannelSummary.ToString":"Discord.Rpc.RpcChannelSummary.yml","Discord.Rpc.RpcDMChannel":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CachedMessages":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.CloseAsync(RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.EnterTypingState(RequestOptions)":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcDMChannel.ToString":"Discord.Rpc.RpcDMChannel.yml","Discord.Rpc.RpcGroupChannel":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.CachedMessages":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.VoiceStates":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.LeaveAsync(RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.EnterTypingState(RequestOptions)":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGroupChannel.ToString":"Discord.Rpc.RpcGroupChannel.yml","Discord.Rpc.RpcGuildChannel":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GuildId":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.Position":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ModifyAsync(Action{GuildChannelProperties},RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.DeleteAsync(RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(IUser,OverwritePermissions,RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.AddPermissionOverwriteAsync(IRole,OverwritePermissions,RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(IUser,RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.RemovePermissionOverwriteAsync(IRole,RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.GetInvitesAsync(RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,RequestOptions)":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcGuildChannel.ToString":"Discord.Rpc.RpcGuildChannel.yml","Discord.Rpc.RpcTextChannel":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.CachedMessages":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.Mention":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.ModifyAsync(Action{TextChannelProperties},RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcTextChannel.EnterTypingState(RequestOptions)":"Discord.Rpc.RpcTextChannel.yml","Discord.Rpc.RpcVoiceChannel":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.UserLimit":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.Bitrate":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.VoiceStates":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcVoiceChannel.ModifyAsync(Action{VoiceChannelProperties},RequestOptions)":"Discord.Rpc.RpcVoiceChannel.yml","Discord.Rpc.RpcGuild":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Name":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.IconUrl":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.Users":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuild.ToString":"Discord.Rpc.RpcGuild.yml","Discord.Rpc.RpcGuildStatus":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Guild":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.Online":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildStatus.ToString":"Discord.Rpc.RpcGuildStatus.yml","Discord.Rpc.RpcGuildSummary":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Id":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.Name":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcGuildSummary.ToString":"Discord.Rpc.RpcGuildSummary.yml","Discord.Rpc.RpcMessage":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Channel":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Author":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Content":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.AuthorColor":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.CreatedAt":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsTTS":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsPinned":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsBlocked":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.EditedTimestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Attachments":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Embeds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedChannelIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedRoleIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.MentionedUserIds":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Tags":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.WebhookId":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.IsWebhook":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.Timestamp":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.DeleteAsync(RequestOptions)":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcMessage.ToString":"Discord.Rpc.RpcMessage.yml","Discord.Rpc.RpcSystemMessage":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcSystemMessage.Type":"Discord.Rpc.RpcSystemMessage.yml","Discord.Rpc.RpcUserMessage":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsTTS":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsPinned":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.IsBlocked":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.WebhookId":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.EditedTimestamp":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Attachments":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Embeds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedChannelIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedRoleIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.MentionedUserIds":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Tags":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Reactions":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.ModifyAsync(Action{MessageProperties},RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(Emoji,RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.AddReactionAsync(System.String,RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(Emoji,IUser,RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveReactionAsync(System.String,IUser,RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.RemoveAllReactionsAsync(RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.PinAsync(RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.UnpinAsync(RequestOptions)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(System.Int32,TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.RpcUserMessage.Resolve(TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.Rpc.RpcUserMessage.yml","Discord.Rpc.Pan":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Left":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.Right":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.#ctor(System.Single,System.Single)":"Discord.Rpc.Pan.yml","Discord.Rpc.Pan.ToString":"Discord.Rpc.Pan.yml","Discord.Rpc.RpcGuildUser":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcGuildUser.Status":"Discord.Rpc.RpcGuildUser.yml","Discord.Rpc.RpcUser":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.IsBot":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Username":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.DiscriminatorValue":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarId":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.AvatarUrl":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreatedAt":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Discriminator":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Mention":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Game":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.Status":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.CreateDMChannelAsync(RequestOptions)":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcUser.ToString":"Discord.Rpc.RpcUser.yml","Discord.Rpc.RpcVoiceState":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.User":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Nickname":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Volume":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted2":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.Pan":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSuppressed":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfMuted":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.IsSelfDeafened":"Discord.Rpc.RpcVoiceState.yml","Discord.Rpc.RpcVoiceState.ToString":"Discord.Rpc.RpcVoiceState.yml","Discord.Commands":"Discord.Commands.yml","Discord.Commands.RpcCommandContext":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.Client":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.Channel":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.User":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.Message":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.IsPrivate":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.RpcCommandContext.#ctor(Discord.Rpc.DiscordRpcClient,Discord.Rpc.RpcUserMessage)":"Discord.Commands.RpcCommandContext.yml","Discord.Commands.ShardedCommandContext":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.Client":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.Guild":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.Channel":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.User":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.Message":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.IsPrivate":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.ShardedCommandContext.#ctor(Discord.WebSocket.DiscordShardedClient,Discord.WebSocket.SocketUserMessage)":"Discord.Commands.ShardedCommandContext.yml","Discord.Commands.SocketCommandContext":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.Client":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.Guild":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.Channel":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.User":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.Message":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.IsPrivate":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.SocketCommandContext.#ctor(Discord.WebSocket.DiscordSocketClient,Discord.WebSocket.SocketUserMessage)":"Discord.Commands.SocketCommandContext.yml","Discord.Commands.CommandContext":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Client":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Guild":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Channel":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.User":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.Message":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.IsPrivate":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandContext.#ctor(IDiscordClient,IUserMessage)":"Discord.Commands.CommandContext.yml","Discord.Commands.CommandError":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnknownCommand":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ParseFailed":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.BadArgCount":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.ObjectNotFound":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.MultipleMatches":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.UnmetPrecondition":"Discord.Commands.CommandError.yml","Discord.Commands.CommandError.Exception":"Discord.Commands.CommandError.yml","Discord.Commands.CommandMatch":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.Command":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.Alias":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.#ctor(Discord.Commands.CommandInfo,System.String)":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.CheckPreconditionsAsync(ICommandContext,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.ParseAsync(ICommandContext,Discord.Commands.SearchResult,System.Nullable{Discord.Commands.PreconditionResult})":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.ExecuteAsync(ICommandContext,IEnumerable{System.Object},IEnumerable{System.Object},Discord.Commands.IDependencyMap)":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandMatch.ExecuteAsync(ICommandContext,Discord.Commands.ParseResult,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandMatch.yml","Discord.Commands.CommandService":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Modules":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Commands":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.TypeReaders":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.#ctor(Discord.Commands.CommandServiceConfig)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.CreateModuleAsync(System.String,Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddModulesAsync(Assembly)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync(Discord.Commands.ModuleInfo)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.RemoveModuleAsync``1":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader``1(Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.AddTypeReader(Type,Discord.Commands.TypeReader)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(ICommandContext,System.Int32)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.Search(ICommandContext,System.String)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(ICommandContext,System.Int32,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandService.ExecuteAsync(ICommandContext,System.String,Discord.Commands.IDependencyMap,Discord.Commands.MultiMatchHandling)":"Discord.Commands.CommandService.yml","Discord.Commands.CommandServiceConfig":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.DefaultRunMode":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.SeparatorChar":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.CommandServiceConfig.CaseSensitiveCommands":"Discord.Commands.CommandServiceConfig.yml","Discord.Commands.ModuleBase":"Discord.Commands.ModuleBase.yml","Discord.Commands.ModuleBase`1":"Discord.Commands.ModuleBase-1.yml","Discord.Commands.ModuleBase`1.Context":"Discord.Commands.ModuleBase-1.yml","Discord.Commands.ModuleBase`1.ReplyAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Commands.ModuleBase-1.yml","Discord.Commands.MultiMatchHandling":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Exception":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.MultiMatchHandling.Best":"Discord.Commands.MultiMatchHandling.yml","Discord.Commands.RunMode":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Default":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Sync":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Mixed":"Discord.Commands.RunMode.yml","Discord.Commands.RunMode.Async":"Discord.Commands.RunMode.yml","Discord.Commands.AliasAttribute":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.Aliases":"Discord.Commands.AliasAttribute.yml","Discord.Commands.AliasAttribute.#ctor(System.String[])":"Discord.Commands.AliasAttribute.yml","Discord.Commands.CommandAttribute":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.Text":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.RunMode":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor":"Discord.Commands.CommandAttribute.yml","Discord.Commands.CommandAttribute.#ctor(System.String)":"Discord.Commands.CommandAttribute.yml","Discord.Commands.DontAutoLoadAttribute":"Discord.Commands.DontAutoLoadAttribute.yml","Discord.Commands.GroupAttribute":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.Prefix":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor":"Discord.Commands.GroupAttribute.yml","Discord.Commands.GroupAttribute.#ctor(System.String)":"Discord.Commands.GroupAttribute.yml","Discord.Commands.NameAttribute":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.Text":"Discord.Commands.NameAttribute.yml","Discord.Commands.NameAttribute.#ctor(System.String)":"Discord.Commands.NameAttribute.yml","Discord.Commands.OverrideTypeReaderAttribute":"Discord.Commands.OverrideTypeReaderAttribute.yml","Discord.Commands.OverrideTypeReaderAttribute.TypeReader":"Discord.Commands.OverrideTypeReaderAttribute.yml","Discord.Commands.OverrideTypeReaderAttribute.#ctor(Type)":"Discord.Commands.OverrideTypeReaderAttribute.yml","Discord.Commands.ParameterPreconditionAttribute":"Discord.Commands.ParameterPreconditionAttribute.yml","Discord.Commands.ParameterPreconditionAttribute.CheckPermissions(ICommandContext,Discord.Commands.ParameterInfo,System.Object,Discord.Commands.IDependencyMap)":"Discord.Commands.ParameterPreconditionAttribute.yml","Discord.Commands.PreconditionAttribute":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PreconditionAttribute.CheckPermissions(ICommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.PreconditionAttribute.yml","Discord.Commands.PriorityAttribute":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.Priority":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.PriorityAttribute.#ctor(System.Int32)":"Discord.Commands.PriorityAttribute.yml","Discord.Commands.RemainderAttribute":"Discord.Commands.RemainderAttribute.yml","Discord.Commands.RemarksAttribute":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.Text":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.RemarksAttribute.#ctor(System.String)":"Discord.Commands.RemarksAttribute.yml","Discord.Commands.SummaryAttribute":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.Text":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.SummaryAttribute.#ctor(System.String)":"Discord.Commands.SummaryAttribute.yml","Discord.Commands.RequireBotPermissionAttribute":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.GuildPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.ChannelPermission":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(GuildPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.#ctor(ChannelPermission)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.RequireBotPermissionAttribute.CheckPermissions(ICommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireBotPermissionAttribute.yml","Discord.Commands.ContextType":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Guild":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.DM":"Discord.Commands.ContextType.yml","Discord.Commands.ContextType.Group":"Discord.Commands.ContextType.yml","Discord.Commands.RequireContextAttribute":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.Contexts":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.#ctor(Discord.Commands.ContextType)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireContextAttribute.CheckPermissions(ICommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireContextAttribute.yml","Discord.Commands.RequireOwnerAttribute":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireOwnerAttribute.CheckPermissions(ICommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireOwnerAttribute.yml","Discord.Commands.RequireUserPermissionAttribute":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.GuildPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.ChannelPermission":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(GuildPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.#ctor(ChannelPermission)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.RequireUserPermissionAttribute.CheckPermissions(ICommandContext,Discord.Commands.CommandInfo,Discord.Commands.IDependencyMap)":"Discord.Commands.RequireUserPermissionAttribute.yml","Discord.Commands.DependencyMap":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Empty":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.#ctor":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Add``1(``0)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get``1":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.Get(Type)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet``1(``0@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.DependencyMap.TryGet(Type,System.Object@)":"Discord.Commands.DependencyMap.yml","Discord.Commands.IDependencyMap":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Add``1(``0)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get``1":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet``1(``0@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.Get(Type)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IDependencyMap.TryGet(Type,System.Object@)":"Discord.Commands.IDependencyMap.yml","Discord.Commands.IEnumerableExtensions":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.IEnumerableExtensions.Permutate``3(IEnumerable{``0},IEnumerable{``1},Func{``0,``1,``2})":"Discord.Commands.IEnumerableExtensions.yml","Discord.Commands.MessageExtensions":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasCharPrefix(IUserMessage,System.Char,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasStringPrefix(IUserMessage,System.String,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.MessageExtensions.HasMentionPrefix(IUserMessage,IUser,System.Int32@)":"Discord.Commands.MessageExtensions.yml","Discord.Commands.CommandInfo":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Module":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Name":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Summary":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Remarks":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Priority":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.HasVarArgs":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.RunMode":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Aliases":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Parameters":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.Preconditions":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.CheckPreconditionsAsync(ICommandContext,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ParseAsync(ICommandContext,System.Int32,Discord.Commands.SearchResult,System.Nullable{Discord.Commands.PreconditionResult})":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ExecuteAsync(ICommandContext,Discord.Commands.ParseResult,Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.CommandInfo.ExecuteAsync(ICommandContext,IEnumerable{System.Object},IEnumerable{System.Object},Discord.Commands.IDependencyMap)":"Discord.Commands.CommandInfo.yml","Discord.Commands.ModuleInfo":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Service":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Name":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Summary":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Remarks":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Aliases":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Commands":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Preconditions":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Submodules":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.Parent":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ModuleInfo.IsSubmodule":"Discord.Commands.ModuleInfo.yml","Discord.Commands.ParameterInfo":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Command":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Name":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Summary":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsOptional":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsRemainder":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.IsMultiple":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Type":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.DefaultValue":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Preconditions":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.CheckPreconditionsAsync(ICommandContext,System.Object[],Discord.Commands.IDependencyMap)":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.Parse(ICommandContext,System.String)":"Discord.Commands.ParameterInfo.yml","Discord.Commands.ParameterInfo.ToString":"Discord.Commands.ParameterInfo.yml","Discord.Commands.TypeReader":"Discord.Commands.TypeReader.yml","Discord.Commands.TypeReader.Read(ICommandContext,System.String)":"Discord.Commands.TypeReader.yml","Discord.Commands.ExecuteResult":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Exception":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.Error":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ErrorReason":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.IsSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromSuccess":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Exception)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ExecuteResult.yml","Discord.Commands.ExecuteResult.ToString":"Discord.Commands.ExecuteResult.yml","Discord.Commands.IResult":"Discord.Commands.IResult.yml","Discord.Commands.IResult.Error":"Discord.Commands.IResult.yml","Discord.Commands.IResult.ErrorReason":"Discord.Commands.IResult.yml","Discord.Commands.IResult.IsSuccess":"Discord.Commands.IResult.yml","Discord.Commands.ParseResult":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ArgValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ParamValues":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.Error":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ErrorReason":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.IsSuccess":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(IReadOnlyList{Discord.Commands.TypeReaderResult},IReadOnlyList{Discord.Commands.TypeReaderResult})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromSuccess(IReadOnlyList{Discord.Commands.TypeReaderValue},IReadOnlyList{Discord.Commands.TypeReaderValue})":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.FromError(Discord.Commands.IResult)":"Discord.Commands.ParseResult.yml","Discord.Commands.ParseResult.ToString":"Discord.Commands.ParseResult.yml","Discord.Commands.PreconditionResult":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.Error":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ErrorReason":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.IsSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromSuccess":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(System.String)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.FromError(Discord.Commands.IResult)":"Discord.Commands.PreconditionResult.yml","Discord.Commands.PreconditionResult.ToString":"Discord.Commands.PreconditionResult.yml","Discord.Commands.SearchResult":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Text":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Commands":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.Error":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ErrorReason":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.IsSuccess":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromSuccess(System.String,IReadOnlyList{Discord.Commands.CommandMatch})":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.FromError(Discord.Commands.IResult)":"Discord.Commands.SearchResult.yml","Discord.Commands.SearchResult.ToString":"Discord.Commands.SearchResult.yml","Discord.Commands.TypeReaderValue":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Value":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.Score":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.#ctor(System.Object,System.Single)":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderValue.ToString":"Discord.Commands.TypeReaderValue.yml","Discord.Commands.TypeReaderResult":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Values":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.Error":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ErrorReason":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.IsSuccess":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(System.Object)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(Discord.Commands.TypeReaderValue)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromSuccess(IReadOnlyCollection{Discord.Commands.TypeReaderValue})":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.CommandError,System.String)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.FromError(Discord.Commands.IResult)":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.TypeReaderResult.ToString":"Discord.Commands.TypeReaderResult.yml","Discord.Commands.ICommandContext":"Discord.Commands.ICommandContext.yml","Discord.Commands.ICommandContext.Client":"Discord.Commands.ICommandContext.yml","Discord.Commands.ICommandContext.Guild":"Discord.Commands.ICommandContext.yml","Discord.Commands.ICommandContext.Channel":"Discord.Commands.ICommandContext.yml","Discord.Commands.ICommandContext.User":"Discord.Commands.ICommandContext.yml","Discord.Commands.ICommandContext.Message":"Discord.Commands.ICommandContext.yml","Discord.WebSocket":"Discord.WebSocket.yml","Discord.WebSocket.DiscordShardedClient":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.Latency":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.CurrentUser":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.Guilds":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.PrivateChannels":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.Shards":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.VoiceRegions":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.#ctor":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.#ctor(Discord.WebSocket.DiscordSocketConfig)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.#ctor(System.Int32[])":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.#ctor(System.Int32[],Discord.WebSocket.DiscordSocketConfig)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.OnLoginAsync(TokenType,System.String)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.OnLogoutAsync":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ConnectAsync(System.Boolean)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.DisconnectAsync":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetShard(System.Int32)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetApplicationInfoAsync":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetGuild(System.UInt64)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.CreateGuildAsync(System.String,IVoiceRegion,Stream)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetChannel(System.UInt64)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetConnectionsAsync":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetInviteAsync(System.String)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetUser(System.UInt64)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetUser(System.String,System.String)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GetVoiceRegion(System.String)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.DownloadAllUsersAsync":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.DownloadUsersAsync(IEnumerable{Discord.WebSocket.SocketGuild})":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.SetStatusAsync(UserStatus)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.SetGameAsync(System.String,System.String,StreamType)":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ChannelCreated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ChannelDestroyed":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ChannelUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.MessageReceived":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.MessageDeleted":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.MessageUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ReactionAdded":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ReactionRemoved":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.ReactionsCleared":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.RoleCreated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.RoleDeleted":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.RoleUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.JoinedGuild":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.LeftGuild":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GuildAvailable":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GuildUnavailable":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GuildMembersDownloaded":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GuildUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserJoined":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserLeft":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserBanned":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserUnbanned":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.GuildMemberUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserPresenceUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserVoiceStateUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.CurrentUserUpdated":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.UserIsTyping":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.RecipientAdded":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordShardedClient.RecipientRemoved":"Discord.WebSocket.DiscordShardedClient.yml","Discord.WebSocket.DiscordSocketClient":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ShardId":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectionState":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Latency":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUser":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Guilds":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.PrivateChannels":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.VoiceRegions":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.#ctor(Discord.WebSocket.DiscordSocketConfig)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLoginAsync(TokenType,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.OnLogoutAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ConnectAsync(System.Boolean)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DisconnectAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetApplicationInfoAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetGuild(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CreateGuildAsync(System.String,IVoiceRegion,Stream)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetChannel(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetConnectionsAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetInviteAsync(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.UInt64)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetUser(System.String,System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GetVoiceRegion(System.String)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadAllUsersAsync":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.DownloadUsersAsync(IEnumerable{Discord.WebSocket.SocketGuild})":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetStatusAsync(UserStatus)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.SetGameAsync(System.String,System.String,StreamType)":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Connected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Disconnected":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.Ready":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LatencyUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelDestroyed":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ChannelUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageReceived":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.MessageUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.ReactionsCleared":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleCreated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleDeleted":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RoleUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.JoinedGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.LeftGuild":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildAvailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUnavailable":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMembersDownloaded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserJoined":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserLeft":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserBanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUnbanned":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.GuildMemberUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserPresenceUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserVoiceStateUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.CurrentUserUpdated":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.UserIsTyping":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientAdded":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketClient.RecipientRemoved":"Discord.WebSocket.DiscordSocketClient.yml","Discord.WebSocket.DiscordSocketConfig":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.GatewayEncoding":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ConnectionTimeout":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.ShardId":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.TotalShards":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.MessageCacheSize":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.LargeThreshold":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.AudioMode":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.WebSocketProvider":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.UdpSocketProvider":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.DownloadUsersOnGuildAvailable":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.DiscordSocketConfig.#ctor":"Discord.WebSocket.DiscordSocketConfig.yml","Discord.WebSocket.SocketEntity`1":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Discord":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.SocketEntity`1.Id":"Discord.WebSocket.SocketEntity-1.yml","Discord.WebSocket.ISocketAudioChannel":"Discord.WebSocket.ISocketAudioChannel.yml","Discord.WebSocket.ISocketAudioChannel.ConnectAsync":"Discord.WebSocket.ISocketAudioChannel.yml","Discord.WebSocket.ISocketMessageChannel":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.CachedMessages":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(System.UInt64,Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetCachedMessages(IMessage,Direction,System.Int32)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketMessageChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.WebSocket.ISocketMessageChannel.yml","Discord.WebSocket.ISocketPrivateChannel":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.ISocketPrivateChannel.Recipients":"Discord.WebSocket.ISocketPrivateChannel.yml","Discord.WebSocket.SocketChannel":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.CreatedAt":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.Users":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketChannel.yml","Discord.WebSocket.SocketDMChannel":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Recipient":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CachedMessages":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Users":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.CloseAsync(RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(System.UInt64,Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetCachedMessages(IMessage,Direction,System.Int32)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.TriggerTypingAsync(RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.EnterTypingState(RequestOptions)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.ToString":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketDMChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketDMChannel.yml","Discord.WebSocket.SocketGroupChannel":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Name":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.CachedMessages":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Users":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.LeaveAsync(RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.ConnectAsync":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(System.UInt64,Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetCachedMessages(IMessage,Direction,System.Int32)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.TriggerTypingAsync(RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.EnterTypingState(RequestOptions)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.ToString":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGroupChannel.Discord#WebSocket#ISocketPrivateChannel#Recipients":"Discord.WebSocket.SocketGroupChannel.yml","Discord.WebSocket.SocketGuildChannel":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Guild":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Name":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Position":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.PermissionOverwrites":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.Users":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ModifyAsync(Action{GuildChannelProperties},RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.DeleteAsync(RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(IUser)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetPermissionOverwrite(IRole)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(IUser,OverwritePermissions,RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.AddPermissionOverwriteAsync(IRole,OverwritePermissions,RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(IUser,RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.RemovePermissionOverwriteAsync(IRole,RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetInvitesAsync(RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,RequestOptions)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketGuildChannel.ToString":"Discord.WebSocket.SocketGuildChannel.yml","Discord.WebSocket.SocketTextChannel":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Topic":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Mention":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.CachedMessages":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.Users":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.ModifyAsync(Action{TextChannelProperties},RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessage(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(System.UInt64,Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetCachedMessages(IMessage,Direction,System.Int32)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.TriggerTypingAsync(RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.EnterTypingState(RequestOptions)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketTextChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketTextChannel.yml","Discord.WebSocket.SocketVoiceChannel":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Bitrate":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.UserLimit":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.Users":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.ModifyAsync(Action{VoiceChannelProperties},RequestOptions)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.ConnectAsync":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketVoiceChannel.GetUser(System.UInt64)":"Discord.WebSocket.SocketVoiceChannel.yml","Discord.WebSocket.SocketGuild":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Name":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKTimeout":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsEmbeddable":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VerificationLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MfaLevel":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultMessageNotifications":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.MemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadedMemberCount":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AFKChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EmbedChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.OwnerId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.VoiceRegionId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreatedAt":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DefaultChannelId":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IconUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SplashUrl":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.HasAllMembers":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.IsSynced":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.SyncPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloaderPromise":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AudioClient":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CurrentUser":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.EveryoneRole":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Channels":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Emojis":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Features":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Users":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.Roles":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DeleteAsync(RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyAsync(Action{GuildProperties},RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyEmbedAsync(Action{GuildEmbedProperties},RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyChannelsAsync(IEnumerable{BulkGuildChannelProperties},RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ModifyRolesAsync(IEnumerable{BulkRoleProperties},RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.LeaveAsync(RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetBansAsync(RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(IUser,System.Int32,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.AddBanAsync(System.UInt64,System.Int32,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(IUser,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.RemoveBanAsync(System.UInt64,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetChannel(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateTextChannelAsync(System.String,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateVoiceChannelAsync(System.String,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetIntegrationsAsync(RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateIntegrationAsync(System.UInt64,System.String,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetInvitesAsync(RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetRole(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.CreateRoleAsync(System.String,System.Nullable{GuildPermissions},System.Nullable{Color},System.Boolean,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.GetUser(System.UInt64)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.PruneUsersAsync(System.Int32,System.Boolean,RequestOptions)":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.DownloadUsersAsync":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketGuild.ToString":"Discord.WebSocket.SocketGuild.yml","Discord.WebSocket.SocketMessage":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Author":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Channel":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Content":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.CreatedAt":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsTTS":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsPinned":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.EditedTimestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Attachments":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Embeds":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedChannels":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedRoles":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.MentionedUsers":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Tags":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.WebhookId":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.IsWebhook":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.Timestamp":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.DeleteAsync(RequestOptions)":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketMessage.ToString":"Discord.WebSocket.SocketMessage.yml","Discord.WebSocket.SocketReaction":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.UserId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.User":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.MessageId":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Message":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Channel":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketReaction.Emoji":"Discord.WebSocket.SocketReaction.yml","Discord.WebSocket.SocketUserMessage":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsTTS":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.IsPinned":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.WebhookId":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.EditedTimestamp":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Attachments":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Embeds":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Tags":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedChannels":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedRoles":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.MentionedUsers":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Reactions":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.ModifyAsync(Action{MessageProperties},RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(Emoji,RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.AddReactionAsync(System.String,RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(Emoji,IUser,RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveReactionAsync(System.String,IUser,RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.RemoveAllReactionsAsync(RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.PinAsync(RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.UnpinAsync(RequestOptions)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(System.Int32,TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketUserMessage.Resolve(TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.WebSocket.SocketUserMessage.yml","Discord.WebSocket.SocketRole":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Guild":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Color":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsHoisted":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsManaged":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsMentionable":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Name":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Permissions":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Position":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CreatedAt":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.IsEveryone":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.Mention":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ModifyAsync(Action{RoleProperties},RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.DeleteAsync(RequestOptions)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.ToString":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketRole.CompareTo(IRole)":"Discord.WebSocket.SocketRole.yml","Discord.WebSocket.SocketGroupUser":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Channel":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.IsBot":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.Username":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.DiscriminatorValue":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGroupUser.AvatarId":"Discord.WebSocket.SocketGroupUser.yml","Discord.WebSocket.SocketGuildUser":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Guild":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Nickname":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsBot":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Username":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.DiscriminatorValue":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.AvatarId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GuildPermissions":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSelfMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsSuppressed":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsDeafened":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.IsMuted":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.JoinedAt":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.RoleIds":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceChannel":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceSessionId":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.VoiceState":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.Hierarchy":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.ModifyAsync(Action{GuildUserProperties},RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.KickAsync(RequestOptions)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketGuildUser.GetPermissions(IGuildChannel)":"Discord.WebSocket.SocketGuildUser.yml","Discord.WebSocket.SocketPresence":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Status":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.Game":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketPresence.ToString":"Discord.WebSocket.SocketPresence.yml","Discord.WebSocket.SocketSelfUser":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Email":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsVerified":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsMfaEnabled":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.IsBot":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.Username":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.DiscriminatorValue":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.AvatarId":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSelfUser.ModifyAsync(Action{SelfUserProperties},RequestOptions)":"Discord.WebSocket.SocketSelfUser.yml","Discord.WebSocket.SocketSimpleUser":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.IsBot":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.Username":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.DiscriminatorValue":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketSimpleUser.AvatarId":"Discord.WebSocket.SocketSimpleUser.yml","Discord.WebSocket.SocketUser":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.IsBot":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Username":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.DiscriminatorValue":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarId":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.AvatarUrl":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreatedAt":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Discriminator":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Mention":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Game":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.Status":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.CreateDMChannelAsync(RequestOptions)":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketUser.ToString":"Discord.WebSocket.SocketUser.yml","Discord.WebSocket.SocketVoiceState":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.Default":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceChannel":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.VoiceSessionId":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSuppressed":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfMuted":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.IsSelfDeafened":"Discord.WebSocket.SocketVoiceState.yml","Discord.WebSocket.SocketVoiceState.ToString":"Discord.WebSocket.SocketVoiceState.yml","Discord.Audio":"Discord.Audio.yml","Discord.Audio.AudioMode":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Disabled":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Outgoing":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Incoming":"Discord.Audio.AudioMode.yml","Discord.Audio.AudioMode.Both":"Discord.Audio.AudioMode.yml","Discord.Audio.OpusApplication":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.Voice":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.MusicOrMixed":"Discord.Audio.OpusApplication.yml","Discord.Audio.OpusApplication.LowLatency":"Discord.Audio.OpusApplication.yml","Discord.Audio.SecretBox":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Encrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.SecretBox.Decrypt(System.Byte[],System.Int32,System.Int32,System.Byte[],System.Int32,System.Byte[],System.Byte[])":"Discord.Audio.SecretBox.yml","Discord.Audio.AudioInStream":"Discord.Audio.AudioInStream.yml","Discord.Audio.AudioOutStream":"Discord.Audio.AudioOutStream.yml","Discord.Audio.AudioOutStream.CanRead":"Discord.Audio.AudioOutStream.yml","Discord.Audio.AudioOutStream.CanSeek":"Discord.Audio.AudioOutStream.yml","Discord.Audio.AudioOutStream.CanWrite":"Discord.Audio.AudioOutStream.yml","Discord.Audio.AudioOutStream.Clear":"Discord.Audio.AudioOutStream.yml","Discord.Audio.AudioOutStream.ClearAsync(CancellationToken)":"Discord.Audio.AudioOutStream.yml","Discord.Audio.IAudioClient":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Connected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Disconnected":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.LatencyUpdated":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.ConnectionState":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.Latency":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.DisconnectAsync":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateOpusStream(System.Int32,System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateDirectOpusStream(System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreatePCMStream(System.Int32,System.Int32,System.Nullable{System.Int32},System.Int32)":"Discord.Audio.IAudioClient.yml","Discord.Audio.IAudioClient.CreateDirectPCMStream(System.Int32,System.Int32,System.Nullable{System.Int32})":"Discord.Audio.IAudioClient.yml","Discord.Commands.Builders":"Discord.Commands.Builders.yml","Discord.Commands.Builders.CommandBuilder":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Module":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Name":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Summary":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Remarks":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.RunMode":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Priority":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Preconditions":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Parameters":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.Aliases":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithName(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithSummary(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithRunMode(Discord.Commands.RunMode)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.WithPriority(System.Int32)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddAliases(System.String[])":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddParameter``1(System.String,Action{Discord.Commands.Builders.ParameterBuilder})":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.CommandBuilder.AddParameter(System.String,Type,Action{Discord.Commands.Builders.ParameterBuilder})":"Discord.Commands.Builders.CommandBuilder.yml","Discord.Commands.Builders.ModuleBuilder":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Service":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Parent":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Name":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Summary":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Remarks":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Commands":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Modules":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Preconditions":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Aliases":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithName(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.WithRemarks(System.String)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddAliases(System.String[])":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddPrecondition(Discord.Commands.PreconditionAttribute)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddCommand(System.String,Func{ICommandContext,System.Object[],Discord.Commands.IDependencyMap,Task},Action{Discord.Commands.Builders.CommandBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.AddModule(System.String,Action{Discord.Commands.Builders.ModuleBuilder})":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ModuleBuilder.Build(Discord.Commands.CommandService)":"Discord.Commands.Builders.ModuleBuilder.yml","Discord.Commands.Builders.ParameterBuilder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Command":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Name":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.ParameterType":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.TypeReader":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsOptional":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsRemainder":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.IsMultiple":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.DefaultValue":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Summary":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.Preconditions":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithSummary(System.String)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithDefault(System.Object)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsOptional(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsRemainder(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.WithIsMultiple(System.Boolean)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord.Commands.Builders.ParameterBuilder.AddPrecondition(Discord.Commands.ParameterPreconditionAttribute)":"Discord.Commands.Builders.ParameterBuilder.yml","Discord":"Discord.yml","Discord.CDN":"Discord.CDN.yml","Discord.CDN.GetApplicationIconUrl(System.UInt64,System.String)":"Discord.CDN.yml","Discord.CDN.GetUserAvatarUrl(System.UInt64,System.String)":"Discord.CDN.yml","Discord.CDN.GetGuildIconUrl(System.UInt64,System.String)":"Discord.CDN.yml","Discord.CDN.GetGuildSplashUrl(System.UInt64,System.String)":"Discord.CDN.yml","Discord.CDN.GetChannelIconUrl(System.UInt64,System.String)":"Discord.CDN.yml","Discord.CDN.GetEmojiUrl(System.UInt64)":"Discord.CDN.yml","Discord.ConnectionState":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnected":"Discord.ConnectionState.yml","Discord.ConnectionState.Connecting":"Discord.ConnectionState.yml","Discord.ConnectionState.Connected":"Discord.ConnectionState.yml","Discord.ConnectionState.Disconnecting":"Discord.ConnectionState.yml","Discord.DiscordConfig":"Discord.DiscordConfig.yml","Discord.DiscordConfig.APIVersion":"Discord.DiscordConfig.yml","Discord.DiscordConfig.Version":"Discord.DiscordConfig.yml","Discord.DiscordConfig.ClientAPIUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.CDNUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.InviteUrl":"Discord.DiscordConfig.yml","Discord.DiscordConfig.DefaultRequestTimeout":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessageSize":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxMessagesPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.MaxUsersPerBatch":"Discord.DiscordConfig.yml","Discord.DiscordConfig.DefaultRetryMode":"Discord.DiscordConfig.yml","Discord.DiscordConfig.LogLevel":"Discord.DiscordConfig.yml","Discord.Format":"Discord.Format.yml","Discord.Format.Bold(System.String)":"Discord.Format.yml","Discord.Format.Italics(System.String)":"Discord.Format.yml","Discord.Format.Underline(System.String)":"Discord.Format.yml","Discord.Format.Strikethrough(System.String)":"Discord.Format.yml","Discord.Format.Code(System.String,System.String)":"Discord.Format.yml","Discord.Format.Sanitize(System.String)":"Discord.Format.yml","Discord.IDiscordClient":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectionState":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CurrentUser":"Discord.IDiscordClient.yml","Discord.IDiscordClient.ConnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.DisconnectAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetApplicationInfoAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetChannelAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetPrivateChannelsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetConnectionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetGuildsAsync(Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.CreateGuildAsync(System.String,Discord.IVoiceRegion,Stream)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetInviteAsync(System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.UInt64,Discord.CacheMode)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetUserAsync(System.String,System.String)":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionsAsync":"Discord.IDiscordClient.yml","Discord.IDiscordClient.GetVoiceRegionAsync(System.String)":"Discord.IDiscordClient.yml","Discord.LoginState":"Discord.LoginState.yml","Discord.LoginState.LoggedOut":"Discord.LoginState.yml","Discord.LoginState.LoggingIn":"Discord.LoginState.yml","Discord.LoginState.LoggedIn":"Discord.LoginState.yml","Discord.LoginState.LoggingOut":"Discord.LoginState.yml","Discord.RequestOptions":"Discord.RequestOptions.yml","Discord.RequestOptions.Default":"Discord.RequestOptions.yml","Discord.RequestOptions.Timeout":"Discord.RequestOptions.yml","Discord.RequestOptions.CancelToken":"Discord.RequestOptions.yml","Discord.RequestOptions.RetryMode":"Discord.RequestOptions.yml","Discord.RequestOptions.HeaderOnly":"Discord.RequestOptions.yml","Discord.RequestOptions.#ctor":"Discord.RequestOptions.yml","Discord.RequestOptions.Clone":"Discord.RequestOptions.yml","Discord.RetryMode":"Discord.RetryMode.yml","Discord.RetryMode.AlwaysFail":"Discord.RetryMode.yml","Discord.RetryMode.RetryTimeouts":"Discord.RetryMode.yml","Discord.RetryMode.RetryRatelimit":"Discord.RetryMode.yml","Discord.RetryMode.Retry502":"Discord.RetryMode.yml","Discord.RetryMode.AlwaysRetry":"Discord.RetryMode.yml","Discord.TokenType":"Discord.TokenType.yml","Discord.TokenType.User":"Discord.TokenType.yml","Discord.TokenType.Bearer":"Discord.TokenType.yml","Discord.TokenType.Bot":"Discord.TokenType.yml","Discord.CacheMode":"Discord.CacheMode.yml","Discord.CacheMode.AllowDownload":"Discord.CacheMode.yml","Discord.CacheMode.CacheOnly":"Discord.CacheMode.yml","Discord.IApplication":"Discord.IApplication.yml","Discord.IApplication.Name":"Discord.IApplication.yml","Discord.IApplication.Description":"Discord.IApplication.yml","Discord.IApplication.RPCOrigins":"Discord.IApplication.yml","Discord.IApplication.Flags":"Discord.IApplication.yml","Discord.IApplication.IconUrl":"Discord.IApplication.yml","Discord.IApplication.Owner":"Discord.IApplication.yml","Discord.IDeletable":"Discord.IDeletable.yml","Discord.IDeletable.DeleteAsync(Discord.RequestOptions)":"Discord.IDeletable.yml","Discord.IEntity`1":"Discord.IEntity-1.yml","Discord.IEntity`1.Id":"Discord.IEntity-1.yml","Discord.Image":"Discord.Image.yml","Discord.Image.Stream":"Discord.Image.yml","Discord.Image.#ctor(Stream)":"Discord.Image.yml","Discord.IMentionable":"Discord.IMentionable.yml","Discord.IMentionable.Mention":"Discord.IMentionable.yml","Discord.ISnowflakeEntity":"Discord.ISnowflakeEntity.yml","Discord.ISnowflakeEntity.CreatedAt":"Discord.ISnowflakeEntity.yml","Discord.IUpdateable":"Discord.IUpdateable.yml","Discord.IUpdateable.UpdateAsync(Discord.RequestOptions)":"Discord.IUpdateable.yml","Discord.BulkGuildChannelProperties":"Discord.BulkGuildChannelProperties.yml","Discord.BulkGuildChannelProperties.Id":"Discord.BulkGuildChannelProperties.yml","Discord.BulkGuildChannelProperties.Position":"Discord.BulkGuildChannelProperties.yml","Discord.BulkGuildChannelProperties.#ctor(System.UInt64,System.Int32)":"Discord.BulkGuildChannelProperties.yml","Discord.Direction":"Discord.Direction.yml","Discord.Direction.Before":"Discord.Direction.yml","Discord.Direction.After":"Discord.Direction.yml","Discord.Direction.Around":"Discord.Direction.yml","Discord.GuildChannelProperties":"Discord.GuildChannelProperties.yml","Discord.GuildChannelProperties.Name":"Discord.GuildChannelProperties.yml","Discord.GuildChannelProperties.Position":"Discord.GuildChannelProperties.yml","Discord.IAudioChannel":"Discord.IAudioChannel.yml","Discord.IChannel":"Discord.IChannel.yml","Discord.IChannel.Name":"Discord.IChannel.yml","Discord.IChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IChannel.yml","Discord.IDMChannel":"Discord.IDMChannel.yml","Discord.IDMChannel.Recipient":"Discord.IDMChannel.yml","Discord.IDMChannel.CloseAsync(Discord.RequestOptions)":"Discord.IDMChannel.yml","Discord.IGroupChannel":"Discord.IGroupChannel.yml","Discord.IGroupChannel.LeaveAsync(Discord.RequestOptions)":"Discord.IGroupChannel.yml","Discord.IGuildChannel":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Position":"Discord.IGuildChannel.yml","Discord.IGuildChannel.Guild":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GuildId":"Discord.IGuildChannel.yml","Discord.IGuildChannel.PermissionOverwrites":"Discord.IGuildChannel.yml","Discord.IGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.ModifyAsync(Action{Discord.GuildChannelProperties},Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IRole)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetPermissionOverwrite(Discord.IUser)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IRole,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.RemovePermissionOverwriteAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IRole,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.AddPermissionOverwriteAsync(Discord.IUser,Discord.OverwritePermissions,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IGuildChannel.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuildChannel.yml","Discord.IMessageChannel":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendMessageAsync(System.String,System.Boolean,Discord.Embed,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessageAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(System.UInt64,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetMessagesAsync(Discord.IMessage,Discord.Direction,System.Int32,Discord.CacheMode,Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.GetPinnedMessagesAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.DeleteMessagesAsync(IEnumerable{Discord.IMessage},Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.TriggerTypingAsync(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IMessageChannel.EnterTypingState(Discord.RequestOptions)":"Discord.IMessageChannel.yml","Discord.IPrivateChannel":"Discord.IPrivateChannel.yml","Discord.IPrivateChannel.Recipients":"Discord.IPrivateChannel.yml","Discord.ITextChannel":"Discord.ITextChannel.yml","Discord.ITextChannel.Topic":"Discord.ITextChannel.yml","Discord.ITextChannel.ModifyAsync(Action{Discord.TextChannelProperties},Discord.RequestOptions)":"Discord.ITextChannel.yml","Discord.IVoiceChannel":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.Bitrate":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.UserLimit":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ModifyAsync(Action{Discord.VoiceChannelProperties},Discord.RequestOptions)":"Discord.IVoiceChannel.yml","Discord.IVoiceChannel.ConnectAsync":"Discord.IVoiceChannel.yml","Discord.TextChannelProperties":"Discord.TextChannelProperties.yml","Discord.TextChannelProperties.Topic":"Discord.TextChannelProperties.yml","Discord.VoiceChannelProperties":"Discord.VoiceChannelProperties.yml","Discord.VoiceChannelProperties.Bitrate":"Discord.VoiceChannelProperties.yml","Discord.VoiceChannelProperties.UserLimit":"Discord.VoiceChannelProperties.yml","Discord.DefaultMessageNotifications":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.AllMessages":"Discord.DefaultMessageNotifications.yml","Discord.DefaultMessageNotifications.MentionsOnly":"Discord.DefaultMessageNotifications.yml","Discord.GuildEmbedProperties":"Discord.GuildEmbedProperties.yml","Discord.GuildEmbedProperties.Enabled":"Discord.GuildEmbedProperties.yml","Discord.GuildEmbedProperties.Channel":"Discord.GuildEmbedProperties.yml","Discord.GuildEmbedProperties.ChannelId":"Discord.GuildEmbedProperties.yml","Discord.GuildEmoji":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Id":"Discord.GuildEmoji.yml","Discord.GuildEmoji.Name":"Discord.GuildEmoji.yml","Discord.GuildEmoji.IsManaged":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RequireColons":"Discord.GuildEmoji.yml","Discord.GuildEmoji.RoleIds":"Discord.GuildEmoji.yml","Discord.GuildEmoji.ToString":"Discord.GuildEmoji.yml","Discord.GuildIntegrationProperties":"Discord.GuildIntegrationProperties.yml","Discord.GuildIntegrationProperties.ExpireBehavior":"Discord.GuildIntegrationProperties.yml","Discord.GuildIntegrationProperties.ExpireGracePeriod":"Discord.GuildIntegrationProperties.yml","Discord.GuildIntegrationProperties.EnableEmoticons":"Discord.GuildIntegrationProperties.yml","Discord.GuildProperties":"Discord.GuildProperties.yml","Discord.GuildProperties.Username":"Discord.GuildProperties.yml","Discord.GuildProperties.Name":"Discord.GuildProperties.yml","Discord.GuildProperties.Region":"Discord.GuildProperties.yml","Discord.GuildProperties.RegionId":"Discord.GuildProperties.yml","Discord.GuildProperties.VerificationLevel":"Discord.GuildProperties.yml","Discord.GuildProperties.DefaultMessageNotifications":"Discord.GuildProperties.yml","Discord.GuildProperties.AfkTimeout":"Discord.GuildProperties.yml","Discord.GuildProperties.Icon":"Discord.GuildProperties.yml","Discord.GuildProperties.Splash":"Discord.GuildProperties.yml","Discord.GuildProperties.AfkChannel":"Discord.GuildProperties.yml","Discord.GuildProperties.AfkChannelId":"Discord.GuildProperties.yml","Discord.GuildProperties.Owner":"Discord.GuildProperties.yml","Discord.GuildProperties.OwnerId":"Discord.GuildProperties.yml","Discord.IBan":"Discord.IBan.yml","Discord.IBan.User":"Discord.IBan.yml","Discord.IBan.Reason":"Discord.IBan.yml","Discord.IGuild":"Discord.IGuild.yml","Discord.IGuild.Name":"Discord.IGuild.yml","Discord.IGuild.AFKTimeout":"Discord.IGuild.yml","Discord.IGuild.IsEmbeddable":"Discord.IGuild.yml","Discord.IGuild.DefaultMessageNotifications":"Discord.IGuild.yml","Discord.IGuild.MfaLevel":"Discord.IGuild.yml","Discord.IGuild.VerificationLevel":"Discord.IGuild.yml","Discord.IGuild.IconId":"Discord.IGuild.yml","Discord.IGuild.IconUrl":"Discord.IGuild.yml","Discord.IGuild.SplashId":"Discord.IGuild.yml","Discord.IGuild.SplashUrl":"Discord.IGuild.yml","Discord.IGuild.Available":"Discord.IGuild.yml","Discord.IGuild.AFKChannelId":"Discord.IGuild.yml","Discord.IGuild.DefaultChannelId":"Discord.IGuild.yml","Discord.IGuild.EmbedChannelId":"Discord.IGuild.yml","Discord.IGuild.OwnerId":"Discord.IGuild.yml","Discord.IGuild.VoiceRegionId":"Discord.IGuild.yml","Discord.IGuild.AudioClient":"Discord.IGuild.yml","Discord.IGuild.EveryoneRole":"Discord.IGuild.yml","Discord.IGuild.Emojis":"Discord.IGuild.yml","Discord.IGuild.Features":"Discord.IGuild.yml","Discord.IGuild.Roles":"Discord.IGuild.yml","Discord.IGuild.ModifyAsync(Action{Discord.GuildProperties},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyEmbedAsync(Action{Discord.GuildEmbedProperties},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyChannelsAsync(IEnumerable{Discord.BulkGuildChannelProperties},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.ModifyRolesAsync(IEnumerable{Discord.BulkRoleProperties},Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.LeaveAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetBansAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(Discord.IUser,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.AddBanAsync(System.UInt64,System.Int32,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(Discord.IUser,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.RemoveBanAsync(System.UInt64,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelsAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetChannelAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateTextChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateVoiceChannelAsync(System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetIntegrationsAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.CreateIntegrationAsync(System.UInt64,System.String,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetInvitesAsync(Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetRole(System.UInt64)":"Discord.IGuild.yml","Discord.IGuild.CreateRoleAsync(System.String,System.Nullable{Discord.GuildPermissions},System.Nullable{Discord.Color},System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUsersAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetUserAsync(System.UInt64,Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.GetCurrentUserAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuild.DownloadUsersAsync":"Discord.IGuild.yml","Discord.IGuild.PruneUsersAsync(System.Int32,System.Boolean,Discord.RequestOptions)":"Discord.IGuild.yml","Discord.IGuildIntegration":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Id":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Name":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Type":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsEnabled":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.IsSyncing":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireBehavior":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.ExpireGracePeriod":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.SyncedAt":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Account":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.Guild":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.GuildId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.RoleId":"Discord.IGuildIntegration.yml","Discord.IGuildIntegration.User":"Discord.IGuildIntegration.yml","Discord.IntegrationAccount":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Id":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.Name":"Discord.IntegrationAccount.yml","Discord.IntegrationAccount.ToString":"Discord.IntegrationAccount.yml","Discord.IUserGuild":"Discord.IUserGuild.yml","Discord.IUserGuild.Name":"Discord.IUserGuild.yml","Discord.IUserGuild.IconUrl":"Discord.IUserGuild.yml","Discord.IUserGuild.IsOwner":"Discord.IUserGuild.yml","Discord.IUserGuild.Permissions":"Discord.IUserGuild.yml","Discord.IVoiceRegion":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Id":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.Name":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsVip":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.IsOptimal":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SampleHostname":"Discord.IVoiceRegion.yml","Discord.IVoiceRegion.SamplePort":"Discord.IVoiceRegion.yml","Discord.MfaLevel":"Discord.MfaLevel.yml","Discord.MfaLevel.Disabled":"Discord.MfaLevel.yml","Discord.MfaLevel.Enabled":"Discord.MfaLevel.yml","Discord.PermissionTarget":"Discord.PermissionTarget.yml","Discord.PermissionTarget.Role":"Discord.PermissionTarget.yml","Discord.PermissionTarget.User":"Discord.PermissionTarget.yml","Discord.VerificationLevel":"Discord.VerificationLevel.yml","Discord.VerificationLevel.None":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Low":"Discord.VerificationLevel.yml","Discord.VerificationLevel.Medium":"Discord.VerificationLevel.yml","Discord.VerificationLevel.High":"Discord.VerificationLevel.yml","Discord.IInvite":"Discord.IInvite.yml","Discord.IInvite.Code":"Discord.IInvite.yml","Discord.IInvite.Url":"Discord.IInvite.yml","Discord.IInvite.Channel":"Discord.IInvite.yml","Discord.IInvite.ChannelId":"Discord.IInvite.yml","Discord.IInvite.Guild":"Discord.IInvite.yml","Discord.IInvite.GuildId":"Discord.IInvite.yml","Discord.IInvite.AcceptAsync(Discord.RequestOptions)":"Discord.IInvite.yml","Discord.IInviteMetadata":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Inviter":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsRevoked":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.IsTemporary":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxAge":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.MaxUses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.Uses":"Discord.IInviteMetadata.yml","Discord.IInviteMetadata.CreatedAt":"Discord.IInviteMetadata.yml","Discord.Embed":"Discord.Embed.yml","Discord.Embed.Type":"Discord.Embed.yml","Discord.Embed.Description":"Discord.Embed.yml","Discord.Embed.Url":"Discord.Embed.yml","Discord.Embed.Title":"Discord.Embed.yml","Discord.Embed.Timestamp":"Discord.Embed.yml","Discord.Embed.Color":"Discord.Embed.yml","Discord.Embed.Image":"Discord.Embed.yml","Discord.Embed.Video":"Discord.Embed.yml","Discord.Embed.Author":"Discord.Embed.yml","Discord.Embed.Footer":"Discord.Embed.yml","Discord.Embed.Provider":"Discord.Embed.yml","Discord.Embed.Thumbnail":"Discord.Embed.yml","Discord.Embed.Fields":"Discord.Embed.yml","Discord.Embed.ToString":"Discord.Embed.yml","Discord.EmbedAuthor":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Name":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.Url":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.IconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ProxyIconUrl":"Discord.EmbedAuthor.yml","Discord.EmbedAuthor.ToString":"Discord.EmbedAuthor.yml","Discord.EmbedField":"Discord.EmbedField.yml","Discord.EmbedField.Name":"Discord.EmbedField.yml","Discord.EmbedField.Value":"Discord.EmbedField.yml","Discord.EmbedField.Inline":"Discord.EmbedField.yml","Discord.EmbedField.ToString":"Discord.EmbedField.yml","Discord.EmbedFooter":"Discord.EmbedFooter.yml","Discord.EmbedFooter.Text":"Discord.EmbedFooter.yml","Discord.EmbedFooter.IconUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ProxyUrl":"Discord.EmbedFooter.yml","Discord.EmbedFooter.ToString":"Discord.EmbedFooter.yml","Discord.EmbedImage":"Discord.EmbedImage.yml","Discord.EmbedImage.Url":"Discord.EmbedImage.yml","Discord.EmbedImage.ProxyUrl":"Discord.EmbedImage.yml","Discord.EmbedImage.Height":"Discord.EmbedImage.yml","Discord.EmbedImage.Width":"Discord.EmbedImage.yml","Discord.EmbedImage.ToString":"Discord.EmbedImage.yml","Discord.EmbedProvider":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Name":"Discord.EmbedProvider.yml","Discord.EmbedProvider.Url":"Discord.EmbedProvider.yml","Discord.EmbedProvider.ToString":"Discord.EmbedProvider.yml","Discord.EmbedThumbnail":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Url":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ProxyUrl":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Height":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.Width":"Discord.EmbedThumbnail.yml","Discord.EmbedThumbnail.ToString":"Discord.EmbedThumbnail.yml","Discord.EmbedVideo":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Url":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Height":"Discord.EmbedVideo.yml","Discord.EmbedVideo.Width":"Discord.EmbedVideo.yml","Discord.EmbedVideo.ToString":"Discord.EmbedVideo.yml","Discord.Emoji":"Discord.Emoji.yml","Discord.Emoji.Id":"Discord.Emoji.yml","Discord.Emoji.Name":"Discord.Emoji.yml","Discord.Emoji.Url":"Discord.Emoji.yml","Discord.Emoji.Parse(System.String)":"Discord.Emoji.yml","Discord.Emoji.TryParse(System.String,Discord.Emoji@)":"Discord.Emoji.yml","Discord.Emoji.ToString":"Discord.Emoji.yml","Discord.IAttachment":"Discord.IAttachment.yml","Discord.IAttachment.Id":"Discord.IAttachment.yml","Discord.IAttachment.Filename":"Discord.IAttachment.yml","Discord.IAttachment.Url":"Discord.IAttachment.yml","Discord.IAttachment.ProxyUrl":"Discord.IAttachment.yml","Discord.IAttachment.Size":"Discord.IAttachment.yml","Discord.IAttachment.Height":"Discord.IAttachment.yml","Discord.IAttachment.Width":"Discord.IAttachment.yml","Discord.IEmbed":"Discord.IEmbed.yml","Discord.IEmbed.Url":"Discord.IEmbed.yml","Discord.IEmbed.Type":"Discord.IEmbed.yml","Discord.IEmbed.Title":"Discord.IEmbed.yml","Discord.IEmbed.Description":"Discord.IEmbed.yml","Discord.IEmbed.Timestamp":"Discord.IEmbed.yml","Discord.IEmbed.Color":"Discord.IEmbed.yml","Discord.IEmbed.Image":"Discord.IEmbed.yml","Discord.IEmbed.Video":"Discord.IEmbed.yml","Discord.IEmbed.Author":"Discord.IEmbed.yml","Discord.IEmbed.Footer":"Discord.IEmbed.yml","Discord.IEmbed.Provider":"Discord.IEmbed.yml","Discord.IEmbed.Thumbnail":"Discord.IEmbed.yml","Discord.IEmbed.Fields":"Discord.IEmbed.yml","Discord.IMessage":"Discord.IMessage.yml","Discord.IMessage.Type":"Discord.IMessage.yml","Discord.IMessage.IsTTS":"Discord.IMessage.yml","Discord.IMessage.IsPinned":"Discord.IMessage.yml","Discord.IMessage.IsWebhook":"Discord.IMessage.yml","Discord.IMessage.Content":"Discord.IMessage.yml","Discord.IMessage.Timestamp":"Discord.IMessage.yml","Discord.IMessage.EditedTimestamp":"Discord.IMessage.yml","Discord.IMessage.Channel":"Discord.IMessage.yml","Discord.IMessage.Author":"Discord.IMessage.yml","Discord.IMessage.WebhookId":"Discord.IMessage.yml","Discord.IMessage.Attachments":"Discord.IMessage.yml","Discord.IMessage.Embeds":"Discord.IMessage.yml","Discord.IMessage.Tags":"Discord.IMessage.yml","Discord.IMessage.MentionedChannelIds":"Discord.IMessage.yml","Discord.IMessage.MentionedRoleIds":"Discord.IMessage.yml","Discord.IMessage.MentionedUserIds":"Discord.IMessage.yml","Discord.IReaction":"Discord.IReaction.yml","Discord.IReaction.Emoji":"Discord.IReaction.yml","Discord.ISystemMessage":"Discord.ISystemMessage.yml","Discord.ITag":"Discord.ITag.yml","Discord.ITag.Index":"Discord.ITag.yml","Discord.ITag.Length":"Discord.ITag.yml","Discord.ITag.Type":"Discord.ITag.yml","Discord.ITag.Key":"Discord.ITag.yml","Discord.ITag.Value":"Discord.ITag.yml","Discord.IUserMessage":"Discord.IUserMessage.yml","Discord.IUserMessage.ModifyAsync(Action{Discord.MessageProperties},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.PinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.UnpinAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Reactions":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(Discord.Emoji,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.AddReactionAsync(System.String,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(Discord.Emoji,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveReactionAsync(System.String,Discord.IUser,Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.RemoveAllReactionsAsync(Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},Discord.RequestOptions)":"Discord.IUserMessage.yml","Discord.IUserMessage.Resolve(Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling,Discord.TagHandling)":"Discord.IUserMessage.yml","Discord.MessageProperties":"Discord.MessageProperties.yml","Discord.MessageProperties.Content":"Discord.MessageProperties.yml","Discord.MessageProperties.Embed":"Discord.MessageProperties.yml","Discord.MessageType":"Discord.MessageType.yml","Discord.MessageType.Default":"Discord.MessageType.yml","Discord.MessageType.RecipientAdd":"Discord.MessageType.yml","Discord.MessageType.RecipientRemove":"Discord.MessageType.yml","Discord.MessageType.Call":"Discord.MessageType.yml","Discord.MessageType.ChannelNameChange":"Discord.MessageType.yml","Discord.MessageType.ChannelIconChange":"Discord.MessageType.yml","Discord.MessageType.ChannelPinnedMessage":"Discord.MessageType.yml","Discord.Tag`1":"Discord.Tag-1.yml","Discord.Tag`1.Type":"Discord.Tag-1.yml","Discord.Tag`1.Index":"Discord.Tag-1.yml","Discord.Tag`1.Length":"Discord.Tag-1.yml","Discord.Tag`1.Key":"Discord.Tag-1.yml","Discord.Tag`1.Value":"Discord.Tag-1.yml","Discord.Tag`1.ToString":"Discord.Tag-1.yml","Discord.Tag`1.Discord#ITag#Value":"Discord.Tag-1.yml","Discord.TagHandling":"Discord.TagHandling.yml","Discord.TagHandling.Ignore":"Discord.TagHandling.yml","Discord.TagHandling.Remove":"Discord.TagHandling.yml","Discord.TagHandling.Name":"Discord.TagHandling.yml","Discord.TagHandling.NameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.FullName":"Discord.TagHandling.yml","Discord.TagHandling.FullNameNoPrefix":"Discord.TagHandling.yml","Discord.TagHandling.Sanitize":"Discord.TagHandling.yml","Discord.TagType":"Discord.TagType.yml","Discord.TagType.UserMention":"Discord.TagType.yml","Discord.TagType.ChannelMention":"Discord.TagType.yml","Discord.TagType.RoleMention":"Discord.TagType.yml","Discord.TagType.EveryoneMention":"Discord.TagType.yml","Discord.TagType.HereMention":"Discord.TagType.yml","Discord.TagType.Emoji":"Discord.TagType.yml","Discord.ChannelPermission":"Discord.ChannelPermission.yml","Discord.ChannelPermission.CreateInstantInvite":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageChannel":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AddReactions":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.SendTTSMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageMessages":"Discord.ChannelPermission.yml","Discord.ChannelPermission.EmbedLinks":"Discord.ChannelPermission.yml","Discord.ChannelPermission.AttachFiles":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ReadMessageHistory":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MentionEveryone":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseExternalEmojis":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Connect":"Discord.ChannelPermission.yml","Discord.ChannelPermission.Speak":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MuteMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.DeafenMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.MoveMembers":"Discord.ChannelPermission.yml","Discord.ChannelPermission.UseVAD":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManagePermissions":"Discord.ChannelPermission.yml","Discord.ChannelPermission.ManageWebhooks":"Discord.ChannelPermission.yml","Discord.ChannelPermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.None":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.All(Discord.IChannel)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.RawValue":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.CreateInstantInvite":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageChannel":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AddReactions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.SendTTSMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageMessages":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.EmbedLinks":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.AttachFiles":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ReadMessageHistory":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MentionEveryone":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseExternalEmojis":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Connect":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Speak":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MuteMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.DeafenMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.MoveMembers":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.UseVAD":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManagePermissions":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ManageWebhooks":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.UInt64)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.Has(Discord.ChannelPermission)":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToList":"Discord.ChannelPermissions.yml","Discord.ChannelPermissions.ToString":"Discord.ChannelPermissions.yml","Discord.GuildPermission":"Discord.GuildPermission.yml","Discord.GuildPermission.CreateInstantInvite":"Discord.GuildPermission.yml","Discord.GuildPermission.KickMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.BanMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.Administrator":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageChannels":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageGuild":"Discord.GuildPermission.yml","Discord.GuildPermission.AddReactions":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.SendTTSMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageMessages":"Discord.GuildPermission.yml","Discord.GuildPermission.EmbedLinks":"Discord.GuildPermission.yml","Discord.GuildPermission.AttachFiles":"Discord.GuildPermission.yml","Discord.GuildPermission.ReadMessageHistory":"Discord.GuildPermission.yml","Discord.GuildPermission.MentionEveryone":"Discord.GuildPermission.yml","Discord.GuildPermission.UseExternalEmojis":"Discord.GuildPermission.yml","Discord.GuildPermission.Connect":"Discord.GuildPermission.yml","Discord.GuildPermission.Speak":"Discord.GuildPermission.yml","Discord.GuildPermission.MuteMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.DeafenMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.MoveMembers":"Discord.GuildPermission.yml","Discord.GuildPermission.UseVAD":"Discord.GuildPermission.yml","Discord.GuildPermission.ChangeNickname":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageNicknames":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageRoles":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageWebhooks":"Discord.GuildPermission.yml","Discord.GuildPermission.ManageEmojis":"Discord.GuildPermission.yml","Discord.GuildPermissions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.None":"Discord.GuildPermissions.yml","Discord.GuildPermissions.All":"Discord.GuildPermissions.yml","Discord.GuildPermissions.RawValue":"Discord.GuildPermissions.yml","Discord.GuildPermissions.CreateInstantInvite":"Discord.GuildPermissions.yml","Discord.GuildPermissions.BanMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.KickMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Administrator":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageChannels":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageGuild":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AddReactions":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.SendTTSMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageMessages":"Discord.GuildPermissions.yml","Discord.GuildPermissions.EmbedLinks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.AttachFiles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ReadMessageHistory":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MentionEveryone":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseExternalEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Connect":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Speak":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MuteMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.DeafenMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.MoveMembers":"Discord.GuildPermissions.yml","Discord.GuildPermissions.UseVAD":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ChangeNickname":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageNicknames":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageRoles":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageWebhooks":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ManageEmojis":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.UInt64)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.#ctor(System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Boolean,System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Boolean,System.Boolean,System.Boolean)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Modify(System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean},System.Nullable{System.Boolean})":"Discord.GuildPermissions.yml","Discord.GuildPermissions.Has(Discord.GuildPermission)":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToList":"Discord.GuildPermissions.yml","Discord.GuildPermissions.ToString":"Discord.GuildPermissions.yml","Discord.Overwrite":"Discord.Overwrite.yml","Discord.Overwrite.TargetId":"Discord.Overwrite.yml","Discord.Overwrite.TargetType":"Discord.Overwrite.yml","Discord.Overwrite.Permissions":"Discord.Overwrite.yml","Discord.Overwrite.#ctor(System.UInt64,Discord.PermissionTarget,Discord.OverwritePermissions)":"Discord.Overwrite.yml","Discord.OverwritePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.InheritAll":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyAll(Discord.IChannel)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AllowValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DenyValue":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.CreateInstantInvite":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageChannel":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AddReactions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.SendTTSMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageMessages":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.EmbedLinks":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.AttachFiles":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ReadMessageHistory":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MentionEveryone":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseExternalEmojis":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Connect":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Speak":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MuteMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.DeafenMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.MoveMembers":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.UseVAD":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManagePermissions":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ManageWebhooks":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(System.UInt64,System.UInt64)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.#ctor(Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue,Discord.PermValue)":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.Modify(System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue},System.Nullable{Discord.PermValue})":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToAllowList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToDenyList":"Discord.OverwritePermissions.yml","Discord.OverwritePermissions.ToString":"Discord.OverwritePermissions.yml","Discord.PermValue":"Discord.PermValue.yml","Discord.PermValue.Allow":"Discord.PermValue.yml","Discord.PermValue.Deny":"Discord.PermValue.yml","Discord.PermValue.Inherit":"Discord.PermValue.yml","Discord.BulkRoleProperties":"Discord.BulkRoleProperties.yml","Discord.BulkRoleProperties.Id":"Discord.BulkRoleProperties.yml","Discord.BulkRoleProperties.#ctor(System.UInt64)":"Discord.BulkRoleProperties.yml","Discord.Color":"Discord.Color.yml","Discord.Color.Default":"Discord.Color.yml","Discord.Color.RawValue":"Discord.Color.yml","Discord.Color.R":"Discord.Color.yml","Discord.Color.G":"Discord.Color.yml","Discord.Color.B":"Discord.Color.yml","Discord.Color.#ctor(System.UInt32)":"Discord.Color.yml","Discord.Color.#ctor(System.Byte,System.Byte,System.Byte)":"Discord.Color.yml","Discord.Color.#ctor(System.Single,System.Single,System.Single)":"Discord.Color.yml","Discord.Color.ToString":"Discord.Color.yml","Discord.IRole":"Discord.IRole.yml","Discord.IRole.Guild":"Discord.IRole.yml","Discord.IRole.Color":"Discord.IRole.yml","Discord.IRole.IsHoisted":"Discord.IRole.yml","Discord.IRole.IsManaged":"Discord.IRole.yml","Discord.IRole.IsMentionable":"Discord.IRole.yml","Discord.IRole.Name":"Discord.IRole.yml","Discord.IRole.Permissions":"Discord.IRole.yml","Discord.IRole.Position":"Discord.IRole.yml","Discord.IRole.ModifyAsync(Action{Discord.RoleProperties},Discord.RequestOptions)":"Discord.IRole.yml","Discord.RoleProperties":"Discord.RoleProperties.yml","Discord.RoleProperties.Name":"Discord.RoleProperties.yml","Discord.RoleProperties.Permissions":"Discord.RoleProperties.yml","Discord.RoleProperties.Position":"Discord.RoleProperties.yml","Discord.RoleProperties.Color":"Discord.RoleProperties.yml","Discord.RoleProperties.Hoist":"Discord.RoleProperties.yml","Discord.RoleProperties.Mentionable":"Discord.RoleProperties.yml","Discord.Game":"Discord.Game.yml","Discord.Game.Name":"Discord.Game.yml","Discord.Game.StreamUrl":"Discord.Game.yml","Discord.Game.StreamType":"Discord.Game.yml","Discord.Game.#ctor(System.String,System.String,Discord.StreamType)":"Discord.Game.yml","Discord.Game.ToString":"Discord.Game.yml","Discord.GuildUserProperties":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.Mute":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.Deaf":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.Nickname":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.Roles":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.RoleIds":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.Channel":"Discord.GuildUserProperties.yml","Discord.GuildUserProperties.ChannelId":"Discord.GuildUserProperties.yml","Discord.IConnection":"Discord.IConnection.yml","Discord.IConnection.Id":"Discord.IConnection.yml","Discord.IConnection.Type":"Discord.IConnection.yml","Discord.IConnection.Name":"Discord.IConnection.yml","Discord.IConnection.IsRevoked":"Discord.IConnection.yml","Discord.IConnection.IntegrationIds":"Discord.IConnection.yml","Discord.IGroupUser":"Discord.IGroupUser.yml","Discord.IGuildUser":"Discord.IGuildUser.yml","Discord.IGuildUser.JoinedAt":"Discord.IGuildUser.yml","Discord.IGuildUser.Nickname":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildPermissions":"Discord.IGuildUser.yml","Discord.IGuildUser.Guild":"Discord.IGuildUser.yml","Discord.IGuildUser.GuildId":"Discord.IGuildUser.yml","Discord.IGuildUser.RoleIds":"Discord.IGuildUser.yml","Discord.IGuildUser.GetPermissions(Discord.IGuildChannel)":"Discord.IGuildUser.yml","Discord.IGuildUser.KickAsync(Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IGuildUser.ModifyAsync(Action{Discord.GuildUserProperties},Discord.RequestOptions)":"Discord.IGuildUser.yml","Discord.IPresence":"Discord.IPresence.yml","Discord.IPresence.Game":"Discord.IPresence.yml","Discord.IPresence.Status":"Discord.IPresence.yml","Discord.ISelfUser":"Discord.ISelfUser.yml","Discord.ISelfUser.Email":"Discord.ISelfUser.yml","Discord.ISelfUser.IsVerified":"Discord.ISelfUser.yml","Discord.ISelfUser.IsMfaEnabled":"Discord.ISelfUser.yml","Discord.ISelfUser.ModifyAsync(Action{Discord.SelfUserProperties},Discord.RequestOptions)":"Discord.ISelfUser.yml","Discord.IUser":"Discord.IUser.yml","Discord.IUser.AvatarId":"Discord.IUser.yml","Discord.IUser.AvatarUrl":"Discord.IUser.yml","Discord.IUser.Discriminator":"Discord.IUser.yml","Discord.IUser.DiscriminatorValue":"Discord.IUser.yml","Discord.IUser.IsBot":"Discord.IUser.yml","Discord.IUser.Username":"Discord.IUser.yml","Discord.IUser.GetDMChannelAsync(Discord.CacheMode,Discord.RequestOptions)":"Discord.IUser.yml","Discord.IUser.CreateDMChannelAsync(Discord.RequestOptions)":"Discord.IUser.yml","Discord.IVoiceState":"Discord.IVoiceState.yml","Discord.IVoiceState.IsDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfDeafened":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSelfMuted":"Discord.IVoiceState.yml","Discord.IVoiceState.IsSuppressed":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceChannel":"Discord.IVoiceState.yml","Discord.IVoiceState.VoiceSessionId":"Discord.IVoiceState.yml","Discord.SelfUserProperties":"Discord.SelfUserProperties.yml","Discord.SelfUserProperties.Username":"Discord.SelfUserProperties.yml","Discord.SelfUserProperties.Avatar":"Discord.SelfUserProperties.yml","Discord.StreamType":"Discord.StreamType.yml","Discord.StreamType.NotStreaming":"Discord.StreamType.yml","Discord.StreamType.Twitch":"Discord.StreamType.yml","Discord.UserStatus":"Discord.UserStatus.yml","Discord.UserStatus.Unknown":"Discord.UserStatus.yml","Discord.UserStatus.Online":"Discord.UserStatus.yml","Discord.UserStatus.Idle":"Discord.UserStatus.yml","Discord.UserStatus.AFK":"Discord.UserStatus.yml","Discord.UserStatus.DoNotDisturb":"Discord.UserStatus.yml","Discord.UserStatus.Invisible":"Discord.UserStatus.yml","Discord.UserStatus.Offline":"Discord.UserStatus.yml","Discord.AsyncEnumerableExtensions":"Discord.AsyncEnumerableExtensions.yml","Discord.AsyncEnumerableExtensions.Flatten``1(IAsyncEnumerable{IReadOnlyCollection{``0}})":"Discord.AsyncEnumerableExtensions.yml","Discord.DiscordClientExtensions":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetPrivateChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetDMChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelAsync(Discord.IDiscordClient,System.UInt64)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetGroupChannelsAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.DiscordClientExtensions.GetOptimalVoiceRegionAsync(Discord.IDiscordClient)":"Discord.DiscordClientExtensions.yml","Discord.GuildExtensions":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetTextChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelAsync(Discord.IGuild,System.UInt64)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetVoiceChannelsAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetAFKChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetDefaultChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetEmbedChannelAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildExtensions.GetOwnerAsync(Discord.IGuild)":"Discord.GuildExtensions.yml","Discord.GuildUserExtensions":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.AddRolesAsync(Discord.IGuildUser,IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,Discord.IRole[])":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.RemoveRolesAsync(Discord.IGuildUser,IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.GuildUserExtensions.ChangeRolesAsync(Discord.IGuildUser,IEnumerable{Discord.IRole},IEnumerable{Discord.IRole})":"Discord.GuildUserExtensions.yml","Discord.LogMessage":"Discord.LogMessage.yml","Discord.LogMessage.Severity":"Discord.LogMessage.yml","Discord.LogMessage.Source":"Discord.LogMessage.yml","Discord.LogMessage.Message":"Discord.LogMessage.yml","Discord.LogMessage.Exception":"Discord.LogMessage.yml","Discord.LogMessage.#ctor(Discord.LogSeverity,System.String,System.String,Exception)":"Discord.LogMessage.yml","Discord.LogMessage.ToString":"Discord.LogMessage.yml","Discord.LogMessage.ToString(StringBuilder,System.Boolean,System.Boolean,DateTimeKind,System.Nullable{System.Int32})":"Discord.LogMessage.yml","Discord.LogSeverity":"Discord.LogSeverity.yml","Discord.LogSeverity.Critical":"Discord.LogSeverity.yml","Discord.LogSeverity.Error":"Discord.LogSeverity.yml","Discord.LogSeverity.Warning":"Discord.LogSeverity.yml","Discord.LogSeverity.Info":"Discord.LogSeverity.yml","Discord.LogSeverity.Verbose":"Discord.LogSeverity.yml","Discord.LogSeverity.Debug":"Discord.LogSeverity.yml","Discord.RpcException":"Discord.RpcException.yml","Discord.RpcException.ErrorCode":"Discord.RpcException.yml","Discord.RpcException.Reason":"Discord.RpcException.yml","Discord.RpcException.#ctor(System.Int32,System.String)":"Discord.RpcException.yml","Discord.MentionUtils":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionUser(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionChannel(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.MentionRole(System.UInt64)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseUser(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseUser(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseChannel(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseChannel(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.MentionUtils.ParseRole(System.String)":"Discord.MentionUtils.yml","Discord.MentionUtils.TryParseRole(System.String,System.UInt64@)":"Discord.MentionUtils.yml","Discord.Optional`1":"Discord.Optional-1.yml","Discord.Optional`1.Unspecified":"Discord.Optional-1.yml","Discord.Optional`1.Value":"Discord.Optional-1.yml","Discord.Optional`1.IsSpecified":"Discord.Optional-1.yml","Discord.Optional`1.#ctor(`0)":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault":"Discord.Optional-1.yml","Discord.Optional`1.GetValueOrDefault(`0)":"Discord.Optional-1.yml","Discord.Optional`1.Equals(System.Object)":"Discord.Optional-1.yml","Discord.Optional`1.GetHashCode":"Discord.Optional-1.yml","Discord.Optional`1.ToString":"Discord.Optional-1.yml","Discord.Optional`1.op_Implicit(`0)~Discord.Optional{`0}":"Discord.Optional-1.yml","Discord.Optional`1.op_Explicit(Discord.Optional{`0})~`0":"Discord.Optional-1.yml","Discord.Optional":"Discord.Optional.yml","Discord.Optional.Create``1":"Discord.Optional.yml","Discord.Optional.Create``1(``0)":"Discord.Optional.yml","Discord.ChannelType":"Discord.ChannelType.yml","Discord.ChannelType.Text":"Discord.ChannelType.yml","Discord.ChannelType.DM":"Discord.ChannelType.yml","Discord.ChannelType.Voice":"Discord.ChannelType.yml","Discord.ChannelType.Group":"Discord.ChannelType.yml","Discord.RestGuildEmbed":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.IsEnabled":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ChannelId":"Discord.RestGuildEmbed.yml","Discord.RestGuildEmbed.ToString":"Discord.RestGuildEmbed.yml","Discord.RestVoiceRegion":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.Name":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsVip":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.IsOptimal":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SampleHostname":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.SamplePort":"Discord.RestVoiceRegion.yml","Discord.RestVoiceRegion.ToString":"Discord.RestVoiceRegion.yml","Discord.Attachment":"Discord.Attachment.yml","Discord.Attachment.Id":"Discord.Attachment.yml","Discord.Attachment.Filename":"Discord.Attachment.yml","Discord.Attachment.Url":"Discord.Attachment.yml","Discord.Attachment.ProxyUrl":"Discord.Attachment.yml","Discord.Attachment.Size":"Discord.Attachment.yml","Discord.Attachment.Height":"Discord.Attachment.yml","Discord.Attachment.Width":"Discord.Attachment.yml","Discord.Attachment.ToString":"Discord.Attachment.yml","Discord.EmbedBuilder":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.#ctor":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Title":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Description":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Url":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ThumbnailUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.ImageUrl":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Timestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Color":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Author":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Footer":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTitle(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithDescription(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithThumbnailUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithImageUrl(System.String)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithCurrentTimestamp":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithTimestamp(DateTimeOffset)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithColor(Color)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(Discord.EmbedAuthorBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithAuthor(Action{Discord.EmbedAuthorBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(Discord.EmbedFooterBuilder)":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.WithFooter(Action{Discord.EmbedFooterBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.AddField(Action{Discord.EmbedFieldBuilder})":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.Build":"Discord.EmbedBuilder.yml","Discord.EmbedBuilder.op_Implicit(Discord.EmbedBuilder)~Embed":"Discord.EmbedBuilder.yml","Discord.EmbedFieldBuilder":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Name":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Value":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.IsInline":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.#ctor":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithName(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithValue(System.String)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.WithIsInline(System.Boolean)":"Discord.EmbedFieldBuilder.yml","Discord.EmbedFieldBuilder.Build":"Discord.EmbedFieldBuilder.yml","Discord.EmbedAuthorBuilder":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Name":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Url":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.IconUrl":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.#ctor":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithName(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.WithIconUrl(System.String)":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedAuthorBuilder.Build":"Discord.EmbedAuthorBuilder.yml","Discord.EmbedFooterBuilder":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.Text":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.IconUrl":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.#ctor":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithText(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.WithIconUrl(System.String)":"Discord.EmbedFooterBuilder.yml","Discord.EmbedFooterBuilder.Build":"Discord.EmbedFooterBuilder.yml","Discord.RestConnection":"Discord.RestConnection.yml","Discord.RestConnection.Id":"Discord.RestConnection.yml","Discord.RestConnection.Type":"Discord.RestConnection.yml","Discord.RestConnection.Name":"Discord.RestConnection.yml","Discord.RestConnection.IsRevoked":"Discord.RestConnection.yml","Discord.RestConnection.IntegrationIds":"Discord.RestConnection.yml","Discord.RestConnection.ToString":"Discord.RestConnection.yml","Discord.Rest":"Discord.Rest.yml","Discord.Rest.BaseDiscordClient":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Log":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedIn":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoggedOut":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginState":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.CurrentUser":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LoginAsync(TokenType,System.String,System.Boolean)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLoginAsync(TokenType,System.String)":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.LogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.OnLogoutAsync":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.BaseDiscordClient.Dispose":"Discord.Rest.BaseDiscordClient.yml","Discord.Rest.DiscordRestClient":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CurrentUser":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.#ctor(Discord.Rest.DiscordRestConfig)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLoginAsync(TokenType,System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.OnLogoutAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetApplicationInfoAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetChannelAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetPrivateChannelsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetConnectionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetInviteAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildEmbedAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildSummariesAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.CreateGuildAsync(System.String,IVoiceRegion,Stream)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetUserAsync(System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetGuildUserAsync(System.UInt64,System.UInt64)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionsAsync":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestClient.GetVoiceRegionAsync(System.String)":"Discord.Rest.DiscordRestClient.yml","Discord.Rest.DiscordRestConfig":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.UserAgent":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.DiscordRestConfig.RestClientProvider":"Discord.Rest.DiscordRestConfig.yml","Discord.Rest.RestApplication":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication._iconId":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Name":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Description":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.RPCOrigins":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Flags":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.Owner":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.CreatedAt":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.IconUrl":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.UpdateAsync":"Discord.Rest.RestApplication.yml","Discord.Rest.RestApplication.ToString":"Discord.Rest.RestApplication.yml","Discord.Rest.RestEntity`1":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Discord":"Discord.Rest.RestEntity-1.yml","Discord.Rest.RestEntity`1.Id":"Discord.Rest.RestEntity-1.yml","Discord.Rest.IRestAudioChannel":"Discord.Rest.IRestAudioChannel.yml","Discord.Rest.IRestMessageChannel":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestMessageChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rest.IRestMessageChannel.yml","Discord.Rest.IRestPrivateChannel":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.IRestPrivateChannel.Recipients":"Discord.Rest.IRestPrivateChannel.yml","Discord.Rest.RestChannel":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.CreatedAt":"Discord.Rest.RestChannel.yml","Discord.Rest.RestChannel.UpdateAsync(RequestOptions)":"Discord.Rest.RestChannel.yml","Discord.Rest.RestDMChannel":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CurrentUser":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Recipient":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Users":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.UpdateAsync(RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.CloseAsync(RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetUser(System.UInt64)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.EnterTypingState(RequestOptions)":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.ToString":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestDMChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestDMChannel.yml","Discord.Rest.RestGroupChannel":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Name":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Users":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.UpdateAsync(RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.LeaveAsync(RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetUser(System.UInt64)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.EnterTypingState(RequestOptions)":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.ToString":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGroupChannel.Discord#Rest#IRestPrivateChannel#Recipients":"Discord.Rest.RestGroupChannel.yml","Discord.Rest.RestGuildChannel":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.PermissionOverwrites":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Name":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.Position":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GuildId":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.UpdateAsync(RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ModifyAsync(Action{GuildChannelProperties},RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.DeleteAsync(RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(IUser)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetPermissionOverwrite(IRole)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(IUser,OverwritePermissions,RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.AddPermissionOverwriteAsync(IRole,OverwritePermissions,RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(IUser,RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.RemovePermissionOverwriteAsync(IRole,RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.GetInvitesAsync(RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.CreateInviteAsync(System.Nullable{System.Int32},System.Nullable{System.Int32},System.Boolean,RequestOptions)":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestGuildChannel.ToString":"Discord.Rest.RestGuildChannel.yml","Discord.Rest.RestTextChannel":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Topic":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.Mention":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.ModifyAsync(Action{TextChannelProperties},RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUserAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetUsersAsync(RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessageAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.Int32,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(System.UInt64,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetMessagesAsync(IMessage,Direction,System.Int32,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.GetPinnedMessagesAsync(RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendMessageAsync(System.String,System.Boolean,Embed,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.SendFileAsync(Stream,System.String,System.String,System.Boolean,RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.DeleteMessagesAsync(IEnumerable{IMessage},RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.TriggerTypingAsync(RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestTextChannel.EnterTypingState(RequestOptions)":"Discord.Rest.RestTextChannel.yml","Discord.Rest.RestVoiceChannel":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.Bitrate":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.UserLimit":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestVoiceChannel.ModifyAsync(Action{VoiceChannelProperties},RequestOptions)":"Discord.Rest.RestVoiceChannel.yml","Discord.Rest.RestBan":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.User":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.Reason":"Discord.Rest.RestBan.yml","Discord.Rest.RestBan.ToString":"Discord.Rest.RestBan.yml","Discord.Rest.RestGuild":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Name":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKTimeout":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IsEmbeddable":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VerificationLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.MfaLevel":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultMessageNotifications":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AFKChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EmbedChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.OwnerId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.VoiceRegionId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreatedAt":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DefaultChannelId":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.IconUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.SplashUrl":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.EveryoneRole":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Roles":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Emojis":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.Features":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.UpdateAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.DeleteAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyAsync(Action{GuildProperties},RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyEmbedAsync(Action{GuildEmbedProperties},RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyChannelsAsync(IEnumerable{BulkGuildChannelProperties},RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ModifyRolesAsync(IEnumerable{BulkRoleProperties},RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.LeaveAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetBansAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(IUser,System.Int32,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.AddBanAsync(System.UInt64,System.Int32,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(IUser,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.RemoveBanAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelsAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetChannelAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateTextChannelAsync(System.String,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateVoiceChannelAsync(System.String,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetIntegrationsAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateIntegrationAsync(System.UInt64,System.String,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetInvitesAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetRole(System.UInt64)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.CreateRoleAsync(System.String,System.Nullable{GuildPermissions},System.Nullable{Color},System.Boolean,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUsersAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetUserAsync(System.UInt64,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.GetCurrentUserAsync(RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.PruneUsersAsync(System.Int32,System.Boolean,RequestOptions)":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuild.ToString":"Discord.Rest.RestGuild.yml","Discord.Rest.RestGuildIntegration":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Name":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Type":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsEnabled":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.IsSyncing":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireBehavior":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ExpireGracePeriod":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.GuildId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.RoleId":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.User":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.Account":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncedAt":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.DeleteAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ModifyAsync(Action{GuildIntegrationProperties})":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.SyncAsync":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestGuildIntegration.ToString":"Discord.Rest.RestGuildIntegration.yml","Discord.Rest.RestUserGuild":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Name":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IsOwner":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.Permissions":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.CreatedAt":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.IconUrl":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.LeaveAsync(RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.DeleteAsync(RequestOptions)":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestUserGuild.ToString":"Discord.Rest.RestUserGuild.yml","Discord.Rest.RestInvite":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildName":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ChannelId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.GuildId":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Code":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.Url":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.UpdateAsync(RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.DeleteAsync(RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.AcceptAsync(RequestOptions)":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInvite.ToString":"Discord.Rest.RestInvite.yml","Discord.Rest.RestInviteMetadata":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsRevoked":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.IsTemporary":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxAge":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.MaxUses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Uses":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.Inviter":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestInviteMetadata.CreatedAt":"Discord.Rest.RestInviteMetadata.yml","Discord.Rest.RestMessage":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Channel":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Author":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Content":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.CreatedAt":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsTTS":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsPinned":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.EditedTimestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Attachments":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Embeds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedChannelIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedRoleIds":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.MentionedUsers":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Tags":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.WebhookId":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.IsWebhook":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.Timestamp":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.UpdateAsync(RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.DeleteAsync(RequestOptions)":"Discord.Rest.RestMessage.yml","Discord.Rest.RestMessage.ToString":"Discord.Rest.RestMessage.yml","Discord.Rest.RestReaction":"Discord.Rest.RestReaction.yml","Discord.Rest.RestReaction.Emoji":"Discord.Rest.RestReaction.yml","Discord.Rest.RestReaction.Count":"Discord.Rest.RestReaction.yml","Discord.Rest.RestReaction.Me":"Discord.Rest.RestReaction.yml","Discord.Rest.RestSystemMessage":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestSystemMessage.Type":"Discord.Rest.RestSystemMessage.yml","Discord.Rest.RestUserMessage":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsTTS":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.IsPinned":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.WebhookId":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.EditedTimestamp":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Attachments":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Embeds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedChannelIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedRoleIds":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.MentionedUsers":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Tags":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Reactions":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.ModifyAsync(Action{MessageProperties},RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(Emoji,RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.AddReactionAsync(System.String,RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(Emoji,IUser,RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveReactionAsync(System.String,IUser,RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.RemoveAllReactionsAsync(RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.GetReactionUsersAsync(System.String,System.Int32,System.Nullable{System.UInt64},RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.PinAsync(RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.UnpinAsync(RequestOptions)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(System.Int32,TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestUserMessage.Resolve(TagHandling,TagHandling,TagHandling,TagHandling,TagHandling)":"Discord.Rest.RestUserMessage.yml","Discord.Rest.RestRole":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Color":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsHoisted":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsManaged":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsMentionable":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Name":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Permissions":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Position":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CreatedAt":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.IsEveryone":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.Mention":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ModifyAsync(Action{RoleProperties},RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.DeleteAsync(RequestOptions)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.CompareTo(IRole)":"Discord.Rest.RestRole.yml","Discord.Rest.RestRole.ToString":"Discord.Rest.RestRole.yml","Discord.Rest.RestGroupUser":"Discord.Rest.RestGroupUser.yml","Discord.Rest.RestGuildUser":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.Nickname":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsDeafened":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.IsMuted":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildId":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GuildPermissions":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.RoleIds":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.JoinedAt":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.UpdateAsync(RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.ModifyAsync(Action{GuildUserProperties},RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.KickAsync(RequestOptions)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestGuildUser.GetPermissions(IGuildChannel)":"Discord.Rest.RestGuildUser.yml","Discord.Rest.RestSelfUser":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.Email":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsVerified":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.IsMfaEnabled":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.UpdateAsync(RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestSelfUser.ModifyAsync(Action{SelfUserProperties},RequestOptions)":"Discord.Rest.RestSelfUser.yml","Discord.Rest.RestUser":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.IsBot":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Username":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.DiscriminatorValue":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarId":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.AvatarUrl":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreatedAt":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Discriminator":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Mention":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Game":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.Status":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.UpdateAsync(RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.CreateDMChannelAsync(RequestOptions)":"Discord.Rest.RestUser.yml","Discord.Rest.RestUser.ToString":"Discord.Rest.RestUser.yml"} \ No newline at end of file diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 1093b7937..4c5100946 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -61,7 +61,7 @@ By now, your module should look like this: [IoC]: https://msdn.microsoft.com/en-us/library/ff921087.aspx [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx -[ModuleBase]: xref:Discord.Commands.ModuleBase-1 +[ModuleBase]: xref:Discord.Commands.ModuleBase`1 ### Adding Commands @@ -125,8 +125,8 @@ will not need to cast them. To reply to messages, you may also invoke [ReplyAsync], instead of accessing the channel through the [Context] and sending a message. -[Context]: xref:Discord.Commands.ModuleBase-1#Discord_Commands_ModuleBase_1_Context -[SocketCommandContext]: Discord.Commands.SocketCommandContext +[Context]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_Context +[SocketCommandContext]: xref:Discord.Commands.SocketCommandContext >![WARNING] >Contexts should **NOT** be mixed! You cannot have one module that @@ -149,7 +149,7 @@ Invoke [CommandService.AddModulesAsync] to discover modules and install them. [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute -[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#AddModulesAsync_System_Reflection_Assembly_ +[CommandService.AddModulesAsync]: xref:Discord_Commands_CommandService#Discord_Commands_CommandService_AddModulesAsync_Assembly_ #### Loading Modules Manually diff --git a/docs/guides/intro.md b/docs/guides/intro.md index 5790734c1..f16bc9883 100644 --- a/docs/guides/intro.md +++ b/docs/guides/intro.md @@ -45,4 +45,6 @@ For more information, go to [MSDN's Async-Await section.](https://msdn.microsoft >[!NOTE] >In previous versions of Discord.Net, you had to hook into the `Ready` and `GuildAvailable` events to determine when your client was ready for use. ->In 1.0, the [ConnectAsync](xref:Discord.DiscordSocketClient#ConnectAsync) method will automatically wait for the Ready event, and for all guilds to stream. To avoid this, pass `false` into `ConnectAsync`. \ No newline at end of file +>In 1.0, the [ConnectAsync] method will automatically wait for the Ready event, and for all guilds to stream. To avoid this, pass `false` into `ConnectAsync`. + +[ConnectAsync]: xref:Discord.WebSocket.DiscordSocketClient#Discord_WebSocket_DiscordSocketClient_ConnectAsync_System_Boolean_ \ No newline at end of file diff --git a/docs/guides/samples/audio_create_ffmpeg.cs b/docs/guides/samples/audio_create_ffmpeg.cs new file mode 100644 index 000000000..e24af088b --- /dev/null +++ b/docs/guides/samples/audio_create_ffmpeg.cs @@ -0,0 +1,11 @@ +private Process CreateStream(string path) +{ + var ffmpeg = new ProcessStartInfo + { + FileName = "ffmpeg", + Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1", + UseShellExecute = false, + RedirectStandardOutput = true, + }; + return Process.Start(ffmpeg); +} \ No newline at end of file diff --git a/docs/guides/samples/audio_ffmpeg.cs b/docs/guides/samples/audio_ffmpeg.cs new file mode 100644 index 000000000..877050caf --- /dev/null +++ b/docs/guides/samples/audio_ffmpeg.cs @@ -0,0 +1,9 @@ +private async Task SendAsync(IAudioClient client, string path) +{ + // Create FFmpeg using the previous example + var ffmpeg = CreateStream(path); + var output = ffmpeg.StandardOutput.BaseStream; + var discord = client.CreatePCMStream(1920); + await output.CopyToAsync(discord); + await discord.FlushAsync(); +} \ No newline at end of file diff --git a/docs/guides/samples/joining_audio.cs b/docs/guides/samples/joining_audio.cs index 52248757f..0cc36978a 100644 --- a/docs/guides/samples/joining_audio.cs +++ b/docs/guides/samples/joining_audio.cs @@ -1,15 +1,10 @@ -// Create an IAudioClient, and store it for later use -private IAudioClient _audio; - -// Create a Join command, that will join the parameter or the user's current voice channel [Command("join")] -public async Task JoinChannel(IUserMessage msg, - IVoiceChannel channel = null) +public async Task JoinChannel(IVoiceChannel channel = null) { // Get the audio channel channel = channel ?? (msg.Author as IGuildUser)?.VoiceChannel; if (channel == null) { await msg.Channel.SendMessageAsync("User must be in a voice channel, or a voice channel must be passed as an argument."); return; } - // Get the IAudioClient by calling the JoinAsync method - _audio = await channel.JoinAsync(); + // For the next step with transmitting audio, you would want to pass this Audio Client in to a service. + var audioClient = await channel.ConnectAsync(); } \ No newline at end of file diff --git a/docs/guides/voice.md b/docs/guides/voice.md index 2606a5f3e..1f09069f5 100644 --- a/docs/guides/voice.md +++ b/docs/guides/voice.md @@ -7,22 +7,105 @@ ## Installation -To use Audio, you must first configure your `DiscordSocketClient` with Audio support. +To use Audio, you must first configure your [DiscordSocketClient] +with Audio support. -In your @Discord.DiscordSocketConfig, set `AudioMode` to the appropriate @Discord.Audio.AudioMode for your bot. For most bots, you will only need to use `AudioMode.Outgoing`. +In your [DiscordSocketConfig], set `AudioMode` to the appropriate +[AudioMode] for your bot. For most bots, you will only need to use +`AudioMode.Outgoing`. + +[DiscordSocketClient]: xref:Discord.WebSocket.DiscordSocketClient +[DiscordSocketConfig]: xref:Discord.WebSocket.DiscordSocketConfig +[AudioMode]: xref:Discord.Audio.AudioMode ### Dependencies -Audio requires two native libraries, `libsodium` and `opus`. Both of these libraries must be placed in the runtime directory of your bot (for .NET 4.6, the directory where your exe is located; for .NET core, directory where your project.json is located) +Audio requires two native libraries, `libsodium` and `opus`. +Both of these libraries must be placed in the runtime directory of your +bot. (When developing on .NET Framework, this would be `bin/debug`, +when developing on .NET Core, this is where you execute `dotnet run` +from; typically the same directory as your csproj). + +For Windows Users, precompiled binaries are available for your +convienence [here](https://discord.foxbot.me/binaries/) -For Windows Users, precompiled binaries are available for your convienence [here](https://discord.foxbot.me/binaries/) +For Linux Users, you will need to compile [Sodium] and [Opus] from +source, or install them from your package manager. -For Linux Users, you will need to compile from source. [Sodium Source Code](https://download.libsodium.org/libsodium/releases/), [Opus Source Code](http://downloads.xiph.org/releases/opus/). +[Sodium]: https://download.libsodium.org/libsodium/releases/ +[Opus]: http://downloads.xiph.org/releases/opus/ ## Joining a Channel -Joining Voice Channels is relatively straight-forward, and is a requirement for sending or receiving audio. This will also allow us to create an @Discord.Audio.IAudioClient, which will be used later to send or receive audio. +Joining a channel is the first step to sending audio, and will return +an [IAudioClient] to send data with. + +To join a channel, simply await [ConnectAsync] on any instance of an +@Discord.IVoiceChannel. [!code-csharp[Joining a Channel](samples/joining_audio.cs)] -The client will sustain a connection to this channel until it is kicked, disconnected from Discord, or told to disconnect. \ No newline at end of file +The client will sustain a connection to this channel until it is +kicked, disconnected from Discord, or told to disconnect. + +It should be noted that voice connections are created on a per-guild +basis; only one audio connection may be open by the bot in a single +guild. To switch channels within a guild, invoke [ConnectAsync] on +another voice channel in the guild. + +[IAudioClient]: xref:Discord.Audio.IAudioClient +[ConnectAsync]: xref:Discord.IVoiceChannel#Discord_IVoiceChannel_ConnectAsync + +## Transmitting Audio + +### With FFmpeg + +[FFmpeg] is an open source, highly versatile AV-muxing tool. This is +the recommended method of transmitting audio. + +Before you begin, you will need to have a version of FFmpeg downloaded +and placed somewhere in your PATH (or alongside the bot, in the same +location as libsodium and opus). Windows binaries are available on +[FFmpeg's download page]. + +[FFmpeg]: https://ffmpeg.org/ +[FFmpeg's download page]: https://ffmpeg.org/download.html + +First, you will need to create a Process that starts FFmpeg. An +example of how to do this is included below, though it is important +that you return PCM at 48000hz. + +>[!NOTE] +>As of the time of this writing, Discord.Audio struggles significantly +>with processing audio that is already opus-encoded; you will need to +>use the PCM write streams. + +[!code-csharp[Creating FFmpeg](samples/audio_create_ffmpeg.cs)] + +Next, to transmit audio from FFmpeg to Discord, you will need to +pull an [AudioOutStream] from your [IAudioClient]. Since we're using +PCM audio, use [IAudioClient.CreatePCMStream]. + +The sample rate argument doesn't particularly matter, so long as it is +a valid rate (120, 240, 480, 960, 1920, or 2880). For the sake of +simplicity, I recommend using 1920. + +Channels should be left at `2`, unless you specified a different value +for `-ac 2` when creating FFmpeg. + +[AudioOutStream]: xref:Discord.Audio.AudioOutStream +[IAudioClient.CreatePCMStream]: xref:Discord.Audio.IAudioClient#Discord_Audio_IAudioClient_CreatePCMStream_System_Int32_System_Int32_System_Nullable_System_Int32__System_Int32_ + +Finally, audio will need to be piped from FFmpeg's stdout into your +AudioOutStream. This step can be as complex as you'd like it to be, but +for the majority of cases, you can just use [Stream.CopyToAsync], as +shown below. + +[Stream.CopyToAsync]: https://msdn.microsoft.com/en-us/library/hh159084(v=vs.110).aspx + +If you are implementing a queue for sending songs, it's likely that +you will want to wait for audio to stop playing before continuing on +to the next song. You can await `AudioOutStream.FlushAsync` to wait for +the audio client's internal buffer to clear out. + +[!code-csharp[Sending Audio](samples/audio_ffmpeg.cs)] \ No newline at end of file From b91026f552371d667b66a254beb10a285ce384a4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 28 Jan 2017 17:04:35 -0400 Subject: [PATCH 195/263] Stop spamming users/@me and voice/regions --- src/Discord.Net.Rest/DiscordRestApiClient.cs | 15 ++++----------- src/Discord.Net.Rest/DiscordRestClient.cs | 7 ++++--- src/Discord.Net.Rpc/DiscordRpcApiClient.cs | 2 +- src/Discord.Net.Rpc/DiscordRpcClient.cs | 1 + .../DiscordShardedClient.cs | 4 ++-- .../DiscordSocketApiClient.cs | 2 +- .../DiscordSocketClient.cs | 18 +++++++++++++----- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 8eec66ec2..065a25b55 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -35,7 +35,6 @@ namespace Discord.API protected bool _isDisposed; private CancellationTokenSource _loginCancelToken; - private bool _fetchCurrentUser; public RetryMode DefaultRetryMode { get; } public string UserAgent { get; } @@ -45,18 +44,15 @@ namespace Discord.API public TokenType AuthTokenType { get; private set; } internal string AuthToken { get; private set; } internal IRestClient RestClient { get; private set; } - internal User CurrentUser { get; private set; } - - public ulong? CurrentUserId => CurrentUser?.Id; + internal ulong? CurrentUserId { get; set;} public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, - JsonSerializer serializer = null, bool fetchCurrentUser = true) + JsonSerializer serializer = null) { RestClientProvider = restClientProvider; UserAgent = userAgent; DefaultRetryMode = defaultRetryMode; _serializer = serializer ?? new JsonSerializer { DateFormatString = "yyyy-MM-ddTHH:mm:ssZ", ContractResolver = new DiscordContractResolver() }; - _fetchCurrentUser = fetchCurrentUser; RequestQueue = new RequestQueue(); _stateLock = new SemaphoreSlim(1, 1); @@ -126,9 +122,6 @@ namespace Discord.API AuthToken = token; RestClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, AuthToken)); - if (_fetchCurrentUser) - CurrentUser = await GetMyUserAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); - LoginState = LoginState.LoggedIn; } catch (Exception) @@ -162,7 +155,7 @@ namespace Discord.API await RequestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false); RestClient.SetCancelToken(CancellationToken.None); - CurrentUser = null; + CurrentUserId = null; LoginState = LoginState.LoggedOut; } @@ -949,7 +942,7 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); options = RequestOptions.CreateOrClone(options); - bool isCurrentUser = userId == CurrentUser.Id; + bool isCurrentUser = userId == CurrentUserId; if (isCurrentUser && args.Nickname.IsSpecified) { diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 384e43821..f5f2cb8b0 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -17,10 +17,11 @@ namespace Discord.Rest private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); - protected override Task OnLoginAsync(TokenType tokenType, string token) + protected override async Task OnLoginAsync(TokenType tokenType, string token) { - base.CurrentUser = RestSelfUser.Create(this, ApiClient.CurrentUser); - return Task.Delay(0); + var user = await ApiClient.GetMyUserAsync(new RequestOptions { RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); + ApiClient.CurrentUserId = user.Id; + base.CurrentUser = RestSelfUser.Create(this, user); } protected override Task OnLogoutAsync() { diff --git a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs index b1ac7121a..8c83d24d6 100644 --- a/src/Discord.Net.Rpc/DiscordRpcApiClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcApiClient.cs @@ -69,7 +69,7 @@ namespace Discord.API public DiscordRpcApiClient(string clientId, string userAgent, string origin, RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) - : base(restClientProvider, userAgent, defaultRetryMode, serializer, false) + : base(restClientProvider, userAgent, defaultRetryMode, serializer) { _connectionLock = new SemaphoreSlim(1, 1); _clientId = clientId; diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index e47cbf30c..845ba97c6 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -435,6 +435,7 @@ namespace Discord.Rpc { var response = await ApiClient.SendAuthenticateAsync(options).ConfigureAwait(false); CurrentUser = RestSelfUser.Create(this, response.User); + ApiClient.CurrentUserId = CurrentUser.Id; ApplicationInfo = RestApplication.Create(this, response.Application); Scopes = response.Scopes; TokenExpiresAt = response.Expires; diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index a32c46f10..3a8f90990 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -64,7 +64,7 @@ namespace Discord.WebSocket _shardIdsToIndex.Add(_shardIds[i], i); var newConfig = config.Clone(); newConfig.ShardId = _shardIds[i]; - _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock); + _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); RegisterEvents(_shards[i]); } } @@ -86,7 +86,7 @@ namespace Discord.WebSocket var newConfig = _baseConfig.Clone(); newConfig.ShardId = _shardIds[i]; newConfig.TotalShards = _totalShards; - _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock); + _shards[i] = new DiscordSocketClient(newConfig, _connectionGroupLock, i != 0 ? _shards[0] : null); RegisterEvents(_shards[i]); } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index 90f7f5c67..fcfa76653 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -35,7 +35,7 @@ namespace Discord.API public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) - : base(restClientProvider, userAgent, defaultRetryMode, serializer, true) + : base(restClientProvider, userAgent, defaultRetryMode, serializer) { WebSocketClient = webSocketProvider(); //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 7591717b6..5ecd48632 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -27,6 +27,7 @@ namespace Discord.WebSocket private readonly Logger _gatewayLogger; private readonly JsonSerializer _serializer; private readonly SemaphoreSlim _connectionGroupLock; + private readonly DiscordSocketClient _parentClient; private string _sessionId; private int _lastSeq; @@ -71,9 +72,9 @@ namespace Discord.WebSocket /// Creates a new REST/WebSocket discord client. public DiscordSocketClient() : this(new DiscordSocketConfig()) { } /// Creates a new REST/WebSocket discord client. - public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null) { } - internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock) : this(config, CreateApiClient(config), groupLock) { } - private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock) + public DiscordSocketClient(DiscordSocketConfig config) : this(config, CreateApiClient(config), null, null) { } + internal DiscordSocketClient(DiscordSocketConfig config, SemaphoreSlim groupLock, DiscordSocketClient parentClient) : this(config, CreateApiClient(config), groupLock, parentClient) { } + private DiscordSocketClient(DiscordSocketConfig config, API.DiscordSocketApiClient client, SemaphoreSlim groupLock, DiscordSocketClient parentClient) : base(config, client) { ShardId = config.ShardId ?? 0; @@ -90,6 +91,7 @@ namespace Discord.WebSocket _nextAudioId = 1; _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); _connectionGroupLock = groupLock; + _parentClient = parentClient; _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => @@ -134,8 +136,13 @@ namespace Discord.WebSocket protected override async Task OnLoginAsync(TokenType tokenType, string token) { - var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); - _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); + if (_parentClient == null) + { + var voiceRegions = await ApiClient.GetVoiceRegionsAsync(new RequestOptions { IgnoreState = true, RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); + _voiceRegions = voiceRegions.Select(x => RestVoiceRegion.Create(this, x)).ToImmutableDictionary(x => x.Id); + } + else + _voiceRegions = _parentClient._voiceRegions; } protected override async Task OnLogoutAsync() { @@ -603,6 +610,7 @@ namespace Discord.WebSocket var state = new ClientState(data.Guilds.Length, data.PrivateChannels.Length); var currentUser = SocketSelfUser.Create(this, state, data.User); + ApiClient.CurrentUserId = currentUser.Id; int unavailableGuilds = 0; for (int i = 0; i < data.Guilds.Length; i++) { From 11a639c5aabe60a0ba39efdc201b0e8041e2db5a Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 28 Jan 2017 19:03:55 -0400 Subject: [PATCH 196/263] Moved a few log events to the shard logger --- src/Discord.Net.Core/Logging/LogManager.cs | 2 +- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 0a2fce738..21f956b99 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -6,7 +6,7 @@ namespace Discord.Logging internal class LogManager { public LogSeverity Level { get; } - public Logger ClientLogger { get; } + private Logger ClientLogger { get; } public event Func Message { add { _messageEvent.Add(value); } remove { _messageEvent.Remove(value); } } private readonly AsyncEvent> _messageEvent = new AsyncEvent>(); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 5ecd48632..c7aea4dfe 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -551,7 +551,7 @@ namespace Discord.WebSocket var data = (payload as JToken).ToObject(_serializer); _heartbeatTime = 0; - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, LogManager.ClientLogger); + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, _gatewayLogger); } break; case GatewayOpCode.Heartbeat: @@ -640,7 +640,7 @@ namespace Discord.WebSocket await SyncGuildsAsync().ConfigureAwait(false); _lastGuildAvailableTime = Environment.TickCount; - _guildDownloadTask = WaitForGuildsAsync(_cancelToken.Token, LogManager.ClientLogger); + _guildDownloadTask = WaitForGuildsAsync(_cancelToken.Token, _gatewayLogger); await _readyEvent.InvokeAsync().ConfigureAwait(false); From 547d7d241f92ab8c7dcbdfae7863b5f37d2b68d5 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 28 Jan 2017 19:04:20 -0400 Subject: [PATCH 197/263] Added a few debug tools for stability testing --- .../Discord.Net.DebugTools.csproj | 31 +++ .../UnstableUdpClient.cs | 142 +++++++++++ .../UnstableUdpClientProvider.cs | 9 + .../UnstableWebSocketClient.cs | 237 ++++++++++++++++++ .../UnstableWebSocketClientProvider.cs | 9 + 5 files changed, 428 insertions(+) create mode 100644 src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj create mode 100644 src/Discord.Net.DebugTools/UnstableUdpClient.cs create mode 100644 src/Discord.Net.DebugTools/UnstableUdpClientProvider.cs create mode 100644 src/Discord.Net.DebugTools/UnstableWebSocketClient.cs create mode 100644 src/Discord.Net.DebugTools/UnstableWebSocketClientProvider.cs diff --git a/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj new file mode 100644 index 000000000..da0c8b0fd --- /dev/null +++ b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj @@ -0,0 +1,31 @@ + + + 1.0.0 + rc-dev + rc-$(BuildNumber) + netstandard1.6 + Discord.Net.DebugTools + RogueException + A Discord.Net extension adding random helper classes for diagnosing issues. + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + Discord + true + + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.DebugTools/UnstableUdpClient.cs b/src/Discord.Net.DebugTools/UnstableUdpClient.cs new file mode 100644 index 000000000..297c689cf --- /dev/null +++ b/src/Discord.Net.DebugTools/UnstableUdpClient.cs @@ -0,0 +1,142 @@ +using Discord.Net.Udp; +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Providers.UnstableUdpSocket +{ + internal class UnstableUdpSocket : IUdpSocket, IDisposable + { + private const double FailureRate = 0.10; //10% + + public event Func ReceivedDatagram; + + private readonly SemaphoreSlim _lock; + private readonly Random _rand; + private UdpClient _udp; + private IPEndPoint _destination; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private Task _task; + private bool _isDisposed; + + public UnstableUdpSocket() + { + _lock = new SemaphoreSlim(1, 1); + _rand = new Random(); + _cancelTokenSource = new CancellationTokenSource(); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + StopInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + + public async Task StartAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StartInternalAsync(_cancelToken).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StartInternalAsync(CancellationToken cancelToken) + { + await StopInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _udp = new UdpClient(0); + + _task = RunAsync(_cancelToken); + } + public async Task StopAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await StopInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + public async Task StopInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_udp != null) + { + try { _udp.Dispose(); } + catch { } + _udp = null; + } + } + + public void SetDestination(string host, int port) + { + var entry = Dns.GetHostEntryAsync(host).GetAwaiter().GetResult(); + _destination = new IPEndPoint(entry.AddressList[0], port); + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count) + { + if (!UnstableCheck()) + return; + + if (index != 0) //Should never happen? + { + var newData = new byte[count]; + Buffer.BlockCopy(data, index, newData, 0, count); + data = newData; + } + + await _udp.SendAsync(data, count, _destination).ConfigureAwait(false); + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var closeTask = Task.Delay(-1, cancelToken); + while (!cancelToken.IsCancellationRequested) + { + var receiveTask = _udp.ReceiveAsync(); + var task = await Task.WhenAny(closeTask, receiveTask).ConfigureAwait(false); + if (task == closeTask) + break; + + var result = receiveTask.Result; + await ReceivedDatagram(result.Buffer, 0, result.Buffer.Length).ConfigureAwait(false); + } + } + + private bool UnstableCheck() + { + return _rand.NextDouble() > FailureRate; + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.DebugTools/UnstableUdpClientProvider.cs b/src/Discord.Net.DebugTools/UnstableUdpClientProvider.cs new file mode 100644 index 000000000..e78514602 --- /dev/null +++ b/src/Discord.Net.DebugTools/UnstableUdpClientProvider.cs @@ -0,0 +1,9 @@ +using Discord.Net.Udp; + +namespace Discord.Net.Providers.UnstableUdpSocket +{ + public static class UnstableUdpSocketProvider + { + public static readonly UdpSocketProvider Instance = () => new UnstableUdpSocket(); + } +} diff --git a/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs b/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs new file mode 100644 index 000000000..e12e103c5 --- /dev/null +++ b/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs @@ -0,0 +1,237 @@ +using Discord.Net.WebSockets; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Net.Providers.UnstableWebSocket +{ + internal class UnstableWebSocketClient : IWebSocketClient, IDisposable + { + public const int ReceiveChunkSize = 16 * 1024; //16KB + public const int SendChunkSize = 4 * 1024; //4KB + private const int HR_TIMEOUT = -2147012894; + private const double FailureRate = 0.10; //10% + + public event Func BinaryMessage; + public event Func TextMessage; + public event Func Closed; + + private readonly SemaphoreSlim _lock; + private readonly Dictionary _headers; + private readonly Random _rand; + private ClientWebSocket _client; + private Task _task; + private CancellationTokenSource _cancelTokenSource; + private CancellationToken _cancelToken, _parentToken; + private bool _isDisposed; + + public UnstableWebSocketClient() + { + _lock = new SemaphoreSlim(1, 1); + _rand = new Random(); + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationToken.None; + _parentToken = CancellationToken.None; + _headers = new Dictionary(); + } + private void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + DisconnectInternalAsync(true).GetAwaiter().GetResult(); + _isDisposed = true; + } + } + public void Dispose() + { + Dispose(true); + } + + public async Task ConnectAsync(string host) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await ConnectInternalAsync(host).ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + private async Task ConnectInternalAsync(string host) + { + await DisconnectInternalAsync().ConfigureAwait(false); + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + + _client = new ClientWebSocket(); + _client.Options.Proxy = null; + _client.Options.KeepAliveInterval = TimeSpan.Zero; + foreach (var header in _headers) + { + if (header.Value != null) + _client.Options.SetRequestHeader(header.Key, header.Value); + } + + await _client.ConnectAsync(new Uri(host), _cancelToken).ConfigureAwait(false); + _task = RunAsync(_cancelToken); + } + + public async Task DisconnectAsync() + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync().ConfigureAwait(false); + } + finally + { + _lock.Release(); + } + } + private async Task DisconnectInternalAsync(bool isDisposing = false) + { + try { _cancelTokenSource.Cancel(false); } catch { } + + if (!isDisposing) + await (_task ?? Task.Delay(0)).ConfigureAwait(false); + + if (_client != null && _client.State == WebSocketState.Open) + { + var token = new CancellationToken(); + if (!isDisposing) + { + try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", token); } + catch { } + } + try { _client.Dispose(); } + catch { } + _client = null; + } + } + + public void SetHeader(string key, string value) + { + _headers[key] = value; + } + public void SetCancelToken(CancellationToken cancelToken) + { + _parentToken = cancelToken; + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_parentToken, _cancelTokenSource.Token).Token; + } + + public async Task SendAsync(byte[] data, int index, int count, bool isText) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + if (!UnstableCheck()) + return; + + if (_client == null) return; + + int frameCount = (int)Math.Ceiling((double)count / SendChunkSize); + + for (int i = 0; i < frameCount; i++, index += SendChunkSize) + { + bool isLast = i == (frameCount - 1); + + int frameSize; + if (isLast) + frameSize = count - (i * SendChunkSize); + else + frameSize = SendChunkSize; + + var type = isText ? WebSocketMessageType.Text : WebSocketMessageType.Binary; + await _client.SendAsync(new ArraySegment(data, index, count), type, isLast, _cancelToken).ConfigureAwait(false); + } + } + finally + { + _lock.Release(); + } + } + + private async Task RunAsync(CancellationToken cancelToken) + { + var buffer = new ArraySegment(new byte[ReceiveChunkSize]); + + try + { + while (!cancelToken.IsCancellationRequested) + { + WebSocketReceiveResult socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); + byte[] result; + int resultCount; + + if (socketResult.MessageType == WebSocketMessageType.Close) + { + var _ = Closed(new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription)); + return; + } + + if (!socketResult.EndOfMessage) + { + //This is a large message (likely just READY), lets create a temporary expandable stream + using (var stream = new MemoryStream()) + { + stream.Write(buffer.Array, 0, socketResult.Count); + do + { + if (cancelToken.IsCancellationRequested) return; + socketResult = await _client.ReceiveAsync(buffer, cancelToken).ConfigureAwait(false); + stream.Write(buffer.Array, 0, socketResult.Count); + } + while (socketResult == null || !socketResult.EndOfMessage); + + //Use the internal buffer if we can get it + resultCount = (int)stream.Length; + ArraySegment streamBuffer; + if (stream.TryGetBuffer(out streamBuffer)) + result = streamBuffer.Array; + else + result = stream.ToArray(); + } + } + else + { + //Small message + resultCount = socketResult.Count; + result = buffer.Array; + } + + if (socketResult.MessageType == WebSocketMessageType.Text) + { + string text = Encoding.UTF8.GetString(result, 0, resultCount); + await TextMessage(text).ConfigureAwait(false); + } + else + await BinaryMessage(result, 0, resultCount).ConfigureAwait(false); + } + } + catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) + { + var _ = Closed(new Exception("Connection timed out.", ex)); + } + catch (OperationCanceledException) { } + catch (Exception ex) + { + //This cannot be awaited otherwise we'll deadlock when DiscordApiClient waits for this task to complete. + var _ = Closed(ex); + } + } + + private bool UnstableCheck() + { + return _rand.NextDouble() > FailureRate; + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.DebugTools/UnstableWebSocketClientProvider.cs b/src/Discord.Net.DebugTools/UnstableWebSocketClientProvider.cs new file mode 100644 index 000000000..9619e8882 --- /dev/null +++ b/src/Discord.Net.DebugTools/UnstableWebSocketClientProvider.cs @@ -0,0 +1,9 @@ +using Discord.Net.WebSockets; + +namespace Discord.Net.Providers.UnstableWebSocket +{ + public static class UnstableWebSocketProvider + { + public static readonly WebSocketProvider Instance = () => new UnstableWebSocketClient(); + } +} From a266d072db675de649a43e46dcdd7c38b9b799c0 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 28 Jan 2017 19:04:42 -0400 Subject: [PATCH 198/263] Fixed nullref during reconnect --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index c7aea4dfe..6f48a23e7 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -313,7 +313,7 @@ namespace Discord.WebSocket private async Task StartReconnectAsync(Exception ex) { - if ((ex as WebSocketClosedException).CloseCode == 4004) //Bad Token + if ((ex as WebSocketClosedException)?.CloseCode == 4004) //Bad Token { _canReconnect = false; _connectTask?.TrySetException(ex); From 35c10a10065651a3dcd11188b367d2f37c9412a4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 28 Jan 2017 20:54:58 -0400 Subject: [PATCH 199/263] Don't throw if websocket close was requested --- .../UnstableWebSocketClient.cs | 41 ++++++++++++++----- .../Net/DefaultWebSocketClient.cs | 40 +++++++++++++----- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs b/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs index e12e103c5..a0f28ba0a 100644 --- a/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs +++ b/src/Discord.Net.DebugTools/UnstableWebSocketClient.cs @@ -28,7 +28,7 @@ namespace Discord.Net.Providers.UnstableWebSocket private Task _task; private CancellationTokenSource _cancelTokenSource; private CancellationToken _cancelToken, _parentToken; - private bool _isDisposed; + private bool _isDisposed, _isDisconnecting; public UnstableWebSocketClient() { @@ -101,22 +101,44 @@ namespace Discord.Net.Providers.UnstableWebSocket { try { _cancelTokenSource.Cancel(false); } catch { } - if (!isDisposing) + _isDisconnecting = true; + try + { await (_task ?? Task.Delay(0)).ConfigureAwait(false); + _task = null; + } + finally { _isDisconnecting = false; } - if (_client != null && _client.State == WebSocketState.Open) + if (_client != null) { - var token = new CancellationToken(); if (!isDisposing) { - try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", token); } + try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", new CancellationToken()); } catch { } } try { _client.Dispose(); } catch { } + _client = null; } } + private async Task OnClosed(Exception ex) + { + if (_isDisconnecting) + return; //Ignore, this disconnect was requested. + + System.Diagnostics.Debug.WriteLine("OnClosed - " + ex.Message); + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync(false); + } + finally + { + _lock.Release(); + } + await Closed(ex); + } public void SetHeader(string key, string value) { @@ -173,10 +195,7 @@ namespace Discord.Net.Providers.UnstableWebSocket int resultCount; if (socketResult.MessageType == WebSocketMessageType.Close) - { - var _ = Closed(new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription)); - return; - } + throw new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription); if (!socketResult.EndOfMessage) { @@ -219,13 +238,13 @@ namespace Discord.Net.Providers.UnstableWebSocket } catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) { - var _ = Closed(new Exception("Connection timed out.", ex)); + var _ = OnClosed(new Exception("Connection timed out.", ex)); } catch (OperationCanceledException) { } catch (Exception ex) { //This cannot be awaited otherwise we'll deadlock when DiscordApiClient waits for this task to complete. - var _ = Closed(ex); + var _ = OnClosed(ex); } } diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index b2ebffabe..aa8bb6986 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -26,7 +26,7 @@ namespace Discord.Net.WebSockets private Task _task; private CancellationTokenSource _cancelTokenSource; private CancellationToken _cancelToken, _parentToken; - private bool _isDisposed; + private bool _isDisposed, _isDisconnecting; public DefaultWebSocketClient() { @@ -98,22 +98,43 @@ namespace Discord.Net.WebSockets { try { _cancelTokenSource.Cancel(false); } catch { } - if (!isDisposing) + _isDisconnecting = true; + try + { await (_task ?? Task.Delay(0)).ConfigureAwait(false); + _task = null; + } + finally { _isDisconnecting = false; } - if (_client != null && _client.State == WebSocketState.Open) + if (_client != null) { - var token = new CancellationToken(); if (!isDisposing) { - try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", token); } + try { await _client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", new CancellationToken()); } catch { } } try { _client.Dispose(); } catch { } + _client = null; } } + private async Task OnClosed(Exception ex) + { + if (_isDisconnecting) + return; //Ignore, this disconnect was requested. + + await _lock.WaitAsync().ConfigureAwait(false); + try + { + await DisconnectInternalAsync(false); + } + finally + { + _lock.Release(); + } + await Closed(ex); + } public void SetHeader(string key, string value) { @@ -167,10 +188,7 @@ namespace Discord.Net.WebSockets int resultCount; if (socketResult.MessageType == WebSocketMessageType.Close) - { - var _ = Closed(new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription)); - return; - } + throw new WebSocketClosedException((int)socketResult.CloseStatus, socketResult.CloseStatusDescription); if (!socketResult.EndOfMessage) { @@ -217,13 +235,13 @@ namespace Discord.Net.WebSockets } catch (Win32Exception ex) when (ex.HResult == HR_TIMEOUT) { - var _ = Closed(new Exception("Connection timed out.", ex)); + var _ = OnClosed(new Exception("Connection timed out.", ex)); } catch (OperationCanceledException) { } catch (Exception ex) { //This cannot be awaited otherwise we'll deadlock when DiscordApiClient waits for this task to complete. - var _ = Closed(ex); + var _ = OnClosed(ex); } } } From d057c1b564cce32c056dfa05936b01a6a8397039 Mon Sep 17 00:00:00 2001 From: Khionu Sybiern Date: Sun, 29 Jan 2017 17:07:41 -0500 Subject: [PATCH 200/263] Update to Docs' Homepage Intended to make the Home more than a placeholder, and more homey --- docs/index.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index b563a983d..7a51becdf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,12 @@ # Discord.Net Documentation -Refer to [The Intro](guides/intro.md) for tutorials on using Discord.Net, or the [API documentation](api/index.md) to review individual objects in the library. +Discord.Net is an Asynchronous C# Library built on the .NET Standard used to connect to the [Discord Chat Platform](https://discordapp.com/). -**Todo:** Put something meaningful here. \ No newline at end of file +If this is your first time using Discord.Net, you should refer to the [Intro](guides/intro.md) for tutorials. More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. + +For additional resources: + - [Discord API Guild](https://discord.gg/discord-api) (Go to the #dotnet_discord-net channel) + - [GitHub Repo](https://github.com/RogueException/Discord.Net/tree/dev) **ToDo:** Point this to Master after Release. + - [NuGet](https://www.nuget.org/packages/Discord.Net/) + - [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) (For Unstable Releases and Community-Contributed Addons) From c2599977a517acb9b7cc72e84ad7683dd08dc5d6 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Mon, 30 Jan 2017 03:14:12 +0100 Subject: [PATCH 201/263] Add BeforeExecute/AfterExecute methods to ModuleBase --- .../Builders/ModuleClassBuilder.cs | 2 ++ src/Discord.Net.Commands/IModuleBase.cs | 4 ++++ src/Discord.Net.Commands/ModuleBase.cs | 12 ++++++++++++ 3 files changed, 18 insertions(+) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 1663b3dba..2962b517f 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -166,10 +166,12 @@ namespace Discord.Commands instance.SetContext(ctx); try { + instance.BeforeExecute(); return method.Invoke(instance, args) as Task ?? Task.Delay(0); } finally { + instance.AfterExecute(); (instance as IDisposable)?.Dispose(); } }; diff --git a/src/Discord.Net.Commands/IModuleBase.cs b/src/Discord.Net.Commands/IModuleBase.cs index e65a059e3..fda768b53 100644 --- a/src/Discord.Net.Commands/IModuleBase.cs +++ b/src/Discord.Net.Commands/IModuleBase.cs @@ -3,5 +3,9 @@ internal interface IModuleBase { void SetContext(ICommandContext context); + + void BeforeExecute(); + + void AfterExecute(); } } diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index 4b8e1727d..f2e885ac8 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -15,6 +15,14 @@ namespace Discord.Commands return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); } + protected void BeforeExecute() + { + } + + protected void AfterExecute() + { + } + //IModuleBase void IModuleBase.SetContext(ICommandContext context) { @@ -23,5 +31,9 @@ namespace Discord.Commands throw new InvalidOperationException($"Invalid context type. Expected {typeof(T).Name}, got {context.GetType().Name}"); Context = newValue; } + + void IModuleBase.BeforeExecute() => BeforeExecute(); + + void IModuleBase.AfterExecute() => AfterExecute(); } } From ea298875af19177afd721de158555ce5ebf4b773 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Mon, 30 Jan 2017 03:17:57 +0100 Subject: [PATCH 202/263] Actually mark the methods virtual #derp --- src/Discord.Net.Commands/ModuleBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/ModuleBase.cs b/src/Discord.Net.Commands/ModuleBase.cs index f2e885ac8..a38ffce06 100644 --- a/src/Discord.Net.Commands/ModuleBase.cs +++ b/src/Discord.Net.Commands/ModuleBase.cs @@ -15,11 +15,11 @@ namespace Discord.Commands return await Context.Channel.SendMessageAsync(message, isTTS, embed, options).ConfigureAwait(false); } - protected void BeforeExecute() + protected virtual void BeforeExecute() { } - protected void AfterExecute() + protected virtual void AfterExecute() { } From 6188ff0a2f5469682f4f89f64708525b18fe21ab Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 29 Jan 2017 23:11:02 -0400 Subject: [PATCH 203/263] Improve platform errors for websocket/udp --- .../DiscordSocketConfig.cs | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 17640e25b..b47f62dca 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -34,22 +34,42 @@ namespace Discord.WebSocket public UdpSocketProvider UdpSocketProvider { get; set; } /// Gets or sets whether or not all users should be downloaded as guilds come available. - public bool DownloadUsersOnGuildAvailable { get; set; } = false; + public bool AlwaysDownloadUsers { get; set; } = false; public DiscordSocketConfig() { #if NETSTANDARD1_3 - WebSocketProvider = () => new DefaultWebSocketClient(); - UdpSocketProvider = () => new DefaultUdpSocket(); + WebSocketProvider = () => + { + try + { + return new DefaultWebSocketClient(); + } + catch (PlatformNotSupportedException ex) + { + throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.", ex); + } + }; + UdpSocketProvider = () => + { + try + { + return new DefaultUdpSocket(); + } + catch (PlatformNotSupportedException ex) + { + throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.", ex); + } + }; #else WebSocketProvider = () => { - throw new InvalidOperationException("The default websocket provider is not supported on this platform.\n" + + throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.\n" + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); }; UdpSocketProvider = () => { - throw new InvalidOperationException("The default UDP provider is not supported on this platform.\n" + + throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.\n" + "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); }; #endif From b00b69234f4078a10f23194079a9016cce61fad4 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 29 Jan 2017 23:15:48 -0400 Subject: [PATCH 204/263] Users can no longer directly request user downloads. --- .../DiscordShardedClient.cs | 14 ++-- .../DiscordSocketClient.cs | 70 +++++++++---------- .../Entities/Guilds/SocketGuild.cs | 2 +- 3 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 3a8f90990..832e35578 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -112,12 +112,12 @@ namespace Discord.WebSocket } /// - public async Task ConnectAsync(bool waitForGuilds = true) + public async Task ConnectAsync() { await _connectionLock.WaitAsync().ConfigureAwait(false); try { - await ConnectInternalAsync(waitForGuilds).ConfigureAwait(false); + await ConnectInternalAsync().ConfigureAwait(false); } catch { @@ -126,10 +126,10 @@ namespace Discord.WebSocket } finally { _connectionLock.Release(); } } - private async Task ConnectInternalAsync(bool waitForGuilds) + private async Task ConnectInternalAsync() { await Task.WhenAll( - _shards.Select(x => x.ConnectAsync(waitForGuilds)) + _shards.Select(x => x.ConnectAsync()) ).ConfigureAwait(false); CurrentUser = _shards[0].CurrentUser; @@ -253,12 +253,6 @@ namespace Discord.WebSocket public RestVoiceRegion GetVoiceRegion(string id) => _shards[0].GetVoiceRegion(id); - /// Downloads the users list for all large guilds. - public async Task DownloadAllUsersAsync() - { - for (int i = 0; i < _shards.Length; i++) - await _shards[i].DownloadAllUsersAsync().ConfigureAwait(false); - } /// Downloads the users list for the provided guilds, if they don't have a complete list. public async Task DownloadUsersAsync(IEnumerable guilds) { diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 6f48a23e7..0641672d2 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -42,6 +42,7 @@ namespace Discord.WebSocket private bool _canReconnect; private DateTimeOffset? _statusSince; private RestApplication _applicationInfo; + private ConcurrentHashSet _downloadUsersFor; /// Gets the shard of of this client. public int ShardId { get; } @@ -61,7 +62,7 @@ namespace Discord.WebSocket internal int ConnectionTimeout { get; private set; } internal UdpSocketProvider UdpSocketProvider { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; } - internal bool DownloadUsersOnGuildAvailable { get; private set; } + internal bool AlwaysDownloadUsers { get; private set; } internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } @@ -84,9 +85,10 @@ namespace Discord.WebSocket AudioMode = config.AudioMode; UdpSocketProvider = config.UdpSocketProvider; WebSocketProvider = config.WebSocketProvider; - DownloadUsersOnGuildAvailable = config.DownloadUsersOnGuildAvailable; + AlwaysDownloadUsers = config.AlwaysDownloadUsers; ConnectionTimeout = config.ConnectionTimeout; State = new ClientState(0, 0); + _downloadUsersFor = new ConcurrentHashSet(); _nextAudioId = 1; _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); @@ -119,14 +121,17 @@ namespace Discord.WebSocket GuildUnavailable += async g => await _gatewayLogger.VerboseAsync($"Disconnected from {g.Name}").ConfigureAwait(false); LatencyUpdated += async (old, val) => await _gatewayLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); - if (DownloadUsersOnGuildAvailable) + GuildAvailable += g => { - GuildAvailable += g => + if (ConnectionState == ConnectionState.Connected && (AlwaysDownloadUsers || _downloadUsersFor.ContainsKey(g.Id))) { - var _ = g.DownloadUsersAsync(); - return Task.Delay(0); - }; - } + if (!g.HasAllMembers) + { + var _ = g.DownloadUsersAsync(); + } + } + return Task.Delay(0); + }; _voiceRegions = ImmutableDictionary.Create(); _largeGuilds = new ConcurrentQueue(); @@ -151,10 +156,11 @@ namespace Discord.WebSocket _applicationInfo = null; _voiceRegions = ImmutableDictionary.Create(); + _downloadUsersFor.Clear(); } /// - public async Task ConnectAsync(bool waitForGuilds = true) + public async Task ConnectAsync() { await _connectionLock.WaitAsync().ConfigureAwait(false); try @@ -162,13 +168,6 @@ namespace Discord.WebSocket await ConnectInternalAsync(false).ConfigureAwait(false); } finally { _connectionLock.Release(); } - - if (waitForGuilds) - { - var downloadTask = _guildDownloadTask; - if (downloadTask != null) - await _guildDownloadTask.ConfigureAwait(false); - } } private async Task ConnectInternalAsync(bool isReconnecting) { @@ -227,6 +226,8 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); ConnectionState = ConnectionState.Connected; await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); + + await ProcessUserDownloadsAsync(_downloadUsersFor.Select(x => GetGuild(x)).Where(x => x != null).ToImmutableArray()).ConfigureAwait(false); } catch (Exception) { @@ -442,31 +443,23 @@ namespace Discord.WebSocket return null; } - /// Downloads the users list for all large guilds. - public Task DownloadAllUsersAsync() - => DownloadUsersAsync(State.Guilds.Where(x => !x.HasAllMembers)); /// Downloads the users list for the provided guilds, if they don't have a complete list. - public async Task DownloadUsersAsync(IEnumerable guilds) + public async Task DownloadUsersAsync(IEnumerable guilds) { - var cachedGuilds = guilds.ToImmutableArray(); - if (cachedGuilds.Length == 0) return; + foreach (var guild in guilds) + _downloadUsersFor.TryAdd(guild.Id); - //Wait for unsynced guilds to sync first. - var unsyncedGuilds = guilds.Select(x => x.SyncPromise).Where(x => !x.IsCompleted).ToImmutableArray(); - if (unsyncedGuilds.Length > 0) - await Task.WhenAll(unsyncedGuilds).ConfigureAwait(false); - - //Download offline members - const short batchSize = 50; - - if (cachedGuilds.Length == 1) + if (ConnectionState == ConnectionState.Connected) { - if (!cachedGuilds[0].HasAllMembers) - await ApiClient.SendRequestMembersAsync(new ulong[] { cachedGuilds[0].Id }).ConfigureAwait(false); - await cachedGuilds[0].DownloaderPromise.ConfigureAwait(false); - return; + //Race condition leads to guilds being requested twice, probably okay + await ProcessUserDownloadsAsync(guilds.Select(x => GetGuild(x.Id)).Where(x => x != null)).ConfigureAwait(false); } + } + private async Task ProcessUserDownloadsAsync(IEnumerable guilds) + { + var cachedGuilds = guilds.ToImmutableArray(); + const short batchSize = 50; ulong[] batchIds = new ulong[Math.Min(batchSize, cachedGuilds.Length)]; Task[] batchTasks = new Task[batchIds.Length]; int batchCount = (cachedGuilds.Length + (batchSize - 1)) / batchSize; @@ -795,6 +788,7 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); + _downloadUsersFor.TryRemove(data.Id); var guild = RemoveGuild(data.Id); if (guild != null) { @@ -1728,6 +1722,12 @@ namespace Discord.WebSocket await logger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); } } + public async Task WaitForGuildsAsync() + { + var downloadTask = _guildDownloadTask; + if (downloadTask != null) + await _guildDownloadTask.ConfigureAwait(false); + } private async Task WaitForGuildsAsync(CancellationToken cancelToken, Logger logger) { //Wait for GUILD_AVAILABLEs diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index c1bec756c..22a4c2a71 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -55,7 +55,7 @@ namespace Discord.WebSocket public SocketTextChannel DefaultChannel => GetTextChannel(Id); public string IconUrl => CDN.GetGuildIconUrl(Id, IconId); public string SplashUrl => CDN.GetGuildSplashUrl(Id, SplashId); - public bool HasAllMembers => _downloaderPromise.Task.IsCompleted; + public bool HasAllMembers => MemberCount == DownloadedMemberCount;// _downloaderPromise.Task.IsCompleted; public bool IsSynced => _syncPromise.Task.IsCompleted; public Task SyncPromise => _syncPromise.Task; public Task DownloaderPromise => _downloaderPromise.Task; From c9b8ed992cd398ae0d1646047362224245e07deb Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 29 Jan 2017 23:16:06 -0400 Subject: [PATCH 205/263] Started placeholders for analyzers --- .../ConfigureAwaitAnalyzer.cs | 55 +++++++++++++++++++ .../Discord.Net.Analyzers.csproj | 32 +++++++++++ 2 files changed, 87 insertions(+) create mode 100644 src/Discord.Net.Analyzers/ConfigureAwaitAnalyzer.cs create mode 100644 src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj diff --git a/src/Discord.Net.Analyzers/ConfigureAwaitAnalyzer.cs b/src/Discord.Net.Analyzers/ConfigureAwaitAnalyzer.cs new file mode 100644 index 000000000..97382f353 --- /dev/null +++ b/src/Discord.Net.Analyzers/ConfigureAwaitAnalyzer.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace RegexAnalyzer +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ConfigureAwaitAnalyzer : DiagnosticAnalyzer + { + public const string DiagnosticId = "ConfigureAwait"; + internal const string Title = "ConfigureAwait was not specified"; + internal const string MessageFormat = "ConfigureAwait error {0}"; + internal const string Description = "ConfigureAwait(false) should be used."; + internal const string Category = "Usage"; + internal static DiagnosticDescriptor Rule = + new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, + Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.InvocationExpression); + } + + private void AnalyzeNode(SyntaxNodeAnalysisContext context) + { + /*var invocationExpr = (InvocationExpressionSyntax)context.Node; + var memberAccessExpr = invocationExpr.Expression as MemberAccessExpressionSyntax; + if (memberAccessExpr?.Name.ToString() != "Match") return; + var memberSymbol = context.SemanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol; + if (!memberSymbol?.ToString().StartsWith("System.Text.RegularExpressions.Regex.Match") ?? true) return; + var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax; + if ((argumentList?.Arguments.Count ?? 0) < 2) return; + var regexLiteral = argumentList.Arguments[1].Expression as LiteralExpressionSyntax; + if (regexLiteral == null) return; + var regexOpt = context.SemanticModel.GetConstantValue(regexLiteral); + if (!regexOpt.HasValue) return; + var regex = regexOpt.Value as string; + if (regex == null) return; + try + { + System.Text.RegularExpressions.Regex.Match("", regex); + } + catch (ArgumentException e) + { + var diagnostic = Diagnostic.Create(Rule, regexLiteral.GetLocation(), e.Message); + context.ReportDiagnostic(diagnostic); + }*/ + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj new file mode 100644 index 000000000..0612e423f --- /dev/null +++ b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj @@ -0,0 +1,32 @@ + + + 1.0.0 + rc-dev + rc-$(BuildNumber) + netstandard1.3 + Discord.Net.Analyzers + RogueException + A Discord.Net extension adding compile-time analysis. + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + Discord.Analyzers + portable-net45+win81 + true + + + + + + + all + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file From 4b92497c3b2bfb5879501cfd777b0b6181e7993b Mon Sep 17 00:00:00 2001 From: Khionu Sybiern Date: Mon, 30 Jan 2017 16:21:18 -0500 Subject: [PATCH 206/263] Amended after feedback --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 7a51becdf..447fccb10 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,12 +1,12 @@ # Discord.Net Documentation -Discord.Net is an Asynchronous C# Library built on the .NET Standard used to connect to the [Discord Chat Platform](https://discordapp.com/). +Discord.Net is an asynchronous, multiplatform .NET Library used to interface with the [Discord API](https://discordapp.com/). If this is your first time using Discord.Net, you should refer to the [Intro](guides/intro.md) for tutorials. More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. For additional resources: - [Discord API Guild](https://discord.gg/discord-api) (Go to the #dotnet_discord-net channel) - - [GitHub Repo](https://github.com/RogueException/Discord.Net/tree/dev) **ToDo:** Point this to Master after Release. + - [GitHub Repo](https://github.com/RogueException/Discord.Net/tree/dev) (**todo:** Point this to Master after Release.) - [NuGet](https://www.nuget.org/packages/Discord.Net/) - [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) (For Unstable Releases and Community-Contributed Addons) From 02969ff7b5653638dfbb17d198ce708a1b12a14d Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Tue, 31 Jan 2017 02:28:53 +0100 Subject: [PATCH 207/263] Update doc samples for correctness, consistency, and a better complete quickstart example. --- docs/guides/samples/dependency_map_setup.cs | 21 ++- docs/guides/samples/dependency_module.cs | 4 +- docs/guides/samples/first-steps.cs | 134 ++++++++++++++++---- docs/guides/samples/logging.cs | 4 +- 4 files changed, 123 insertions(+), 40 deletions(-) diff --git a/docs/guides/samples/dependency_map_setup.cs b/docs/guides/samples/dependency_map_setup.cs index 1d25db43c..aa39150e7 100644 --- a/docs/guides/samples/dependency_map_setup.cs +++ b/docs/guides/samples/dependency_map_setup.cs @@ -7,18 +7,13 @@ public class Commands { public async Task Install(DiscordSocketClient client) { - var commands = new CommandService(); - var map = new DependencyMap(); - map.Add(client); - map.Add(commands); - await commands.AddModulesAsync(Assembly.GetEntryAssembly()); + // Here, we will inject the Dependency Map with + // all of the services our client will use. + _map.Add(client); + _map.Add(commands); + _map.Add(new NotificationService(_map)); + _map.Add(new DatabaseService(_map)); + // ... + await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); } - // In ConfigureServices, we will inject the Dependency Map with - // all of the services our client will use. - public Task ConfigureServices(IDependencyMap map) - { - map.Add(new NotificationService(map)); - map.Add(new DatabaseService(map)); - } - // ... } diff --git a/docs/guides/samples/dependency_module.cs b/docs/guides/samples/dependency_module.cs index 2e1d662f8..998722b2e 100644 --- a/docs/guides/samples/dependency_module.cs +++ b/docs/guides/samples/dependency_module.cs @@ -23,9 +23,9 @@ public class ModuleB private CommandService _commands; private NotificationService _notification; - public ModuleB(CommandService commands, IDependencyMap map) + public ModuleB(CommandService commands, NotificationService notifications) { _commands = commands; - _notification = map.Get(); + _notification = notifications; } } \ No newline at end of file diff --git a/docs/guides/samples/first-steps.cs b/docs/guides/samples/first-steps.cs index f811f186b..3d89de7d8 100644 --- a/docs/guides/samples/first-steps.cs +++ b/docs/guides/samples/first-steps.cs @@ -1,35 +1,123 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; using Discord; +using Discord.Commands; using Discord.WebSocket; class Program { - // Convert our sync-main to an async main method - static void Main(string[] args) => new Program().Run().GetAwaiter().GetResult(); + private readonly DiscordSocketClient _client; + + // Keep the CommandService and IDependencyMap around for use with commands. + private readonly IDependencyMap _map = new DependencyMap(); + private readonly CommandService _commands = new CommandService(); - // Create a DiscordClient with WebSocket support - private DiscordSocketClient client; + // Program entry point + static void Main(string[] args) + { + // Call the Program constructor, followed by the + // AsyncMain method and wait until it finishes (which should be never). + new Program().AsyncMain().GetAwaiter().GetResult(); + } + + private Program() + { + _client = new DiscordSocketClient(new DiscordSocketConfig + { + // How much logging do you want to see? + LogLevel = LogSeverity.Info, + + // If your platform doesn't have native websockets, + // add Discord.Net.Providers.WS4Net from NuGet, + // add the `using` at the top, and uncomment this line: + //WebSocketProvider = WS4NetProvider.Instance + }); + } - public async Task Run() + // Create a named logging handler, so it can be re-used by addons + // that ask for a Func. + private static Task Logger(LogMessage message) { - client = new DiscordSocketClient(); + var cc = Console.ForegroundColor; + switch (message.Severity) + { + case LogSeverity.Critical: + case LogSeverity.Error: + Console.ForegroundColor = ConsoleColor.Red; + break; + case LogSeverity.Warning: + Console.ForegroundColor = ConsoleColor.Yellow; + break; + case LogSeverity.Info: + Console.ForegroundColor = ConsoleColor.White; + break; + case LogSeverity.Verbose: + case LogSeverity.Debug: + Console.ForegroundColor = ConsoleColor.DarkGray; + break; + } + Console.WriteLine($"{DateTime.Now,-19} [{message.Severity,8}] {message.Source}: {message.Message}"); + Console.ForegroundColor = cc; + return Task.CompletedTask; + } + + private async Task AsyncMain() + { + // Subscribe the logging handler. + _client.Log += Logger; + + // Centralize the logic for commands into a seperate method. + await InitCommands(); + + // Login and connect. + await _client.LoginAsync(TokenType.Bot, /* */); + await _client.ConnectAsync(); - // Place the token of your bot account here - string token = "aaabbbccc"; - - // Hook into the MessageReceived event on DiscordSocketClient - client.MessageReceived += async (message) => - { // Check to see if the Message Content is "!ping" - if (message.Content == "!ping") - // Send 'pong' back to the channel the message was sent in - await message.Channel.SendMessageAsync("pong"); - }; - - // Configure the client to use a Bot token, and use our token - await client.LoginAsync(TokenType.Bot, token); - // Connect the client to Discord's gateway - await client.ConnectAsync(); - - // Block this task until the program is exited. + // Wait infinitely so your bot actually stays connected. await Task.Delay(-1); } + + private async Task InitCommands() + { + // Repeat this for all the service classes + // and other dependencies that your commands might need. + _map.Add(new SomeServiceClass()); + + // Either search the program and add all Module classes that can be found: + await _commands.AddModulesAsync(Assembly.GetEntryAssembly()); + // Or add Modules manually if you prefer to be a little more explicit: + await _commands.AddModuleAsync(); + + // Subscribe a handler to see if a message invokes a command. + _client.MessageReceived += CmdHandler; + } + + private async Task CmdHandler(SocketMessage arg) + { + // Bail out if it's a System Message. + var msg = arg as SocketUserMessage; + if (msg == null) return; + + // Create a number to track where the prefix ends and the command begins + int pos = 0; + // Replace the '!' with whatever character + // you want to prefix your commands with. + // Uncomment the second half if you also want + // commands to be invoked by mentioning the bot instead. + if (msg.HasCharPrefix('!', ref pos) /* || msg.HasMentionPrefix(msg.Discord.CurrentUser, ref pos) */) + { + // Create a Command Context + var context = new SocketCommandContext(msg.Discord, msg); + + // Execute the command. (result does not indicate a return value, + // rather an object stating if the command executed succesfully). + var result = await _commands.ExecuteAsync(context, pos, _map); + + // Uncomment the following lines if you want the bot + // to send a message if it failed (not advised for most situations). + //if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) + // await msg.Channel.SendMessageAsync(result.ErrorReason); + } + } } \ No newline at end of file diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 5155bacdc..24ab76e9b 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -3,13 +3,13 @@ using Discord.Rest; public class Program { - // Note: This is the light client, it only supports REST calls. + // Note: This is the REST client, it only supports REST calls. private DiscordClient _client; static void Main(string[] args) => new Program().Start().GetAwaiter().GetResult(); public async Task Start() { - _client = new DiscordClient(new DiscordConfig() { + _client = new DiscordRestClient(new DiscordConfig() { LogLevel = LogSeverity.Info }); From c7bb1cdf31661ab5329570c423f97c240ae67fc1 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Tue, 31 Jan 2017 02:36:57 +0100 Subject: [PATCH 208/263] Fix whitespace --- docs/guides/samples/first-steps.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/samples/first-steps.cs b/docs/guides/samples/first-steps.cs index 3d89de7d8..933f695b0 100644 --- a/docs/guides/samples/first-steps.cs +++ b/docs/guides/samples/first-steps.cs @@ -99,7 +99,7 @@ class Program var msg = arg as SocketUserMessage; if (msg == null) return; - // Create a number to track where the prefix ends and the command begins + // Create a number to track where the prefix ends and the command begins int pos = 0; // Replace the '!' with whatever character // you want to prefix your commands with. From af33689d1e60ebb875292dba86e99efb3d36ca5e Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Tue, 31 Jan 2017 02:47:05 +0100 Subject: [PATCH 209/263] Respond to feedback --- docs/guides/samples/first-steps.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/samples/first-steps.cs b/docs/guides/samples/first-steps.cs index 933f695b0..01544e03f 100644 --- a/docs/guides/samples/first-steps.cs +++ b/docs/guides/samples/first-steps.cs @@ -17,8 +17,8 @@ class Program static void Main(string[] args) { // Call the Program constructor, followed by the - // AsyncMain method and wait until it finishes (which should be never). - new Program().AsyncMain().GetAwaiter().GetResult(); + // MainAsync method and wait until it finishes (which should be never). + new Program().MainAsync().GetAwaiter().GetResult(); } private Program() @@ -62,7 +62,7 @@ class Program return Task.CompletedTask; } - private async Task AsyncMain() + private async Task MainAsync() { // Subscribe the logging handler. _client.Log += Logger; From f80687ef0e24ab105dcf67b7457a45a19faf4fd5 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Wed, 1 Feb 2017 04:06:24 +0100 Subject: [PATCH 210/263] Fix ParameterInfo.Type sometimes being null --- src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 2962b517f..82850b091 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -210,6 +210,8 @@ namespace Discord.Commands } } + builder.ParameterType = paramType; + if (builder.TypeReader == null) { var readers = service.GetTypeReaders(paramType); @@ -220,7 +222,6 @@ namespace Discord.Commands else reader = service.GetDefaultTypeReader(paramType); - builder.ParameterType = paramType; builder.TypeReader = reader; } } From 05fcd5f07637823a073a7a731083e0ff5af13fa0 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Tue, 29 Nov 2016 00:37:45 +0100 Subject: [PATCH 211/263] First commit --- src/Discord.Net.Core/Utils/Cache.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/Discord.Net.Core/Utils/Cache.cs diff --git a/src/Discord.Net.Core/Utils/Cache.cs b/src/Discord.Net.Core/Utils/Cache.cs new file mode 100644 index 000000000..f61c41793 --- /dev/null +++ b/src/Discord.Net.Core/Utils/Cache.cs @@ -0,0 +1,23 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using TId = Discord.IEntity; + + +namespace Discord +{ + public abstract class Cache where T : IEntity + { + public bool IsCached => Value != null; + public TId Id { get; } + public T Value { get; } + + protected Cache(TId id) + { + Id = id; + } + + public async Task DownloadAsync() => typeof(T).GetRuntimeMethod("DownloadAsync",{ulong id}); + public async Task GetOrDownloadAsync() => IsCached ? Value : await DownloadAsync(); + } +} \ No newline at end of file From 959d49a26f8ad7c181373e1efbbb6574ac4837d5 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Thu, 1 Dec 2016 11:35:45 +0100 Subject: [PATCH 212/263] renamed Cache to Cached, and refactored many events to use Cached --- src/Discord.Net.Core/Utils/Cache.cs | 23 ---- src/Discord.Net.Core/Utils/Cached.cs | 33 ++++++ .../DiscordSocketClient.Events.cs | 32 +++--- .../DiscordSocketClient.cs | 103 ++++++------------ 4 files changed, 85 insertions(+), 106 deletions(-) delete mode 100644 src/Discord.Net.Core/Utils/Cache.cs create mode 100644 src/Discord.Net.Core/Utils/Cached.cs diff --git a/src/Discord.Net.Core/Utils/Cache.cs b/src/Discord.Net.Core/Utils/Cache.cs deleted file mode 100644 index f61c41793..000000000 --- a/src/Discord.Net.Core/Utils/Cache.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Reflection; -using System.Threading.Tasks; -using TId = Discord.IEntity; - - -namespace Discord -{ - public abstract class Cache where T : IEntity - { - public bool IsCached => Value != null; - public TId Id { get; } - public T Value { get; } - - protected Cache(TId id) - { - Id = id; - } - - public async Task DownloadAsync() => typeof(T).GetRuntimeMethod("DownloadAsync",{ulong id}); - public async Task GetOrDownloadAsync() => IsCached ? Value : await DownloadAsync(); - } -} \ No newline at end of file diff --git a/src/Discord.Net.Core/Utils/Cached.cs b/src/Discord.Net.Core/Utils/Cached.cs new file mode 100644 index 000000000..99c7794f7 --- /dev/null +++ b/src/Discord.Net.Core/Utils/Cached.cs @@ -0,0 +1,33 @@ +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public struct Cached where T : IMessage + { + public bool IsCached => !EqualityComparer.Default.Equals(Value, default(T)); + public bool IsDownloadable { get; } + public ulong Id { get; } + public T Value { get; } + public ISocketMessageChannel Channel { get; } + + public Cached(ulong id, T value, ISocketMessageChannel channel, bool isDownloadable = true) + { + Id = id; + Value = value; + Channel = channel; + IsDownloadable = isDownloadable; + } + + public async Task DownloadAsync() + { + if (IsDownloadable) + return (T) await Channel.GetMessageAsync(Id); + throw new InvalidOperationException("This message cannot be downloaded."); + } + + public async Task GetOrDownloadAsync() => IsCached ? Value : await DownloadAsync(); + } +} diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 2f952bdaa..48f945b0c 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -59,36 +59,36 @@ namespace Discord.WebSocket remove { _messageReceivedEvent.Remove(value); } } private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); - public event Func, Task> MessageDeleted + public event Func, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>(); - public event Func, SocketMessage, Task> MessageUpdated + private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>(); + public event Func, SocketMessage, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); - public event Func, SocketReaction, Task> ReactionAdded + private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); + public event Func, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } remove { _reactionAddedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, SocketReaction, Task> ReactionRemoved + private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, SocketReaction, Task> ReactionRemoved { add { _reactionRemovedEvent.Add(value); } remove { _reactionRemovedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, Task> ReactionsCleared + private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); + public event Func, Task> ReactionsCleared { add { _reactionsClearedEvent.Add(value); } remove { _reactionsClearedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); + private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); //Roles public event Func RoleCreated @@ -116,37 +116,37 @@ namespace Discord.WebSocket add { _joinedGuildEvent.Add(value); } remove { _joinedGuildEvent.Remove(value); } } - private AsyncEvent> _joinedGuildEvent = new AsyncEvent>(); + private readonly AsyncEvent> _joinedGuildEvent = new AsyncEvent>(); public event Func LeftGuild { add { _leftGuildEvent.Add(value); } remove { _leftGuildEvent.Remove(value); } } - private AsyncEvent> _leftGuildEvent = new AsyncEvent>(); + private readonly AsyncEvent> _leftGuildEvent = new AsyncEvent>(); public event Func GuildAvailable { add { _guildAvailableEvent.Add(value); } remove { _guildAvailableEvent.Remove(value); } } - private AsyncEvent> _guildAvailableEvent = new AsyncEvent>(); + private readonly AsyncEvent> _guildAvailableEvent = new AsyncEvent>(); public event Func GuildUnavailable { add { _guildUnavailableEvent.Add(value); } remove { _guildUnavailableEvent.Remove(value); } } - private AsyncEvent> _guildUnavailableEvent = new AsyncEvent>(); + private readonly AsyncEvent> _guildUnavailableEvent = new AsyncEvent>(); public event Func GuildMembersDownloaded { add { _guildMembersDownloadedEvent.Add(value); } remove { _guildMembersDownloadedEvent.Remove(value); } } - private AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _guildMembersDownloadedEvent = new AsyncEvent>(); public event Func GuildUpdated { add { _guildUpdatedEvent.Add(value); } remove { _guildUpdatedEvent.Remove(value); } } - private AsyncEvent> _guildUpdatedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _guildUpdatedEvent = new AsyncEvent>(); //Users public event Func UserJoined diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 0641672d2..2d510790f 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -108,11 +108,11 @@ namespace Discord.WebSocket { if (ex != null) { - await _gatewayLogger.WarningAsync($"Connection Closed", ex).ConfigureAwait(false); + await _gatewayLogger.WarningAsync("Connection Closed", ex).ConfigureAwait(false); await StartReconnectAsync(ex).ConfigureAwait(false); } else - await _gatewayLogger.WarningAsync($"Connection Closed").ConfigureAwait(false); + await _gatewayLogger.WarningAsync("Connection Closed").ConfigureAwait(false); }; LeftGuild += async g => await _gatewayLogger.InfoAsync($"Left {g.Name}").ConfigureAwait(false); @@ -408,7 +408,7 @@ namespace Discord.WebSocket /// public SocketUser GetUser(string username, string discriminator) { - return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault(); + return State.Users.FirstOrDefault(x => x.Discriminator == discriminator && x.Username == username); } internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model) { @@ -770,7 +770,7 @@ namespace Discord.WebSocket if (data.Unavailable == true) { type = "GUILD_UNAVAILABLE"; - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); var guild = State.GetGuild(data.Id); if (guild != null) @@ -780,13 +780,13 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.WarningAsync($"GUILD_UNAVAILABLE referenced an unknown guild.").ConfigureAwait(false); + await _gatewayLogger.WarningAsync("GUILD_UNAVAILABLE referenced an unknown guild.").ConfigureAwait(false); return; } } else { - await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); _downloadUsersFor.TryRemove(data.Id); var guild = RemoveGuild(data.Id); @@ -797,7 +797,7 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.WarningAsync($"GUILD_DELETE referenced an unknown guild.").ConfigureAwait(false); + await _gatewayLogger.WarningAsync("GUILD_DELETE referenced an unknown guild.").ConfigureAwait(false); return; } } @@ -810,7 +810,7 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); - SocketChannel channel = null; + SocketChannel channel; if (data.GuildId.IsSpecified) { var guild = State.GetGuild(data.GuildId.Value); @@ -867,7 +867,7 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false); - SocketChannel channel = null; + SocketChannel channel; var data = (payload as JToken).ToObject(_serializer); if (data.GuildId.IsSpecified) { @@ -1205,9 +1205,8 @@ namespace Discord.WebSocket return; } - SocketUser user = State.GetUser(data.User.Id); - if (user == null) - user = SocketSimpleUser.Create(this, State, data.User); + SocketUser user = State.GetUser(data.User.Id) ?? + (SocketUser) SocketSimpleUser.Create(this, State, data.User); await _userUnbannedEvent.InvokeAsync(user, guild).ConfigureAwait(false); } else @@ -1234,13 +1233,8 @@ namespace Discord.WebSocket return; } - SocketUser author; - if (guild != null) - author = guild.GetUser(data.Author.Value.Id); - else - author = (channel as SocketChannel).GetUser(data.Author.Value.Id); - if (author == null) - author = SocketSimpleUser.Create(this, State, data.Author.Value); + var author = (guild != null ? guild.GetUser(data.Author.Value.Id) : (channel as SocketChannel).GetUser(data.Author.Value.Id)) ?? + SocketSimpleUser.Create(this, State, data.Author.Value); if (author != null) { @@ -1287,25 +1281,17 @@ namespace Discord.WebSocket else if (data.Author.IsSpecified) { //Edited message isnt in cache, create a detached one - SocketUser author; - if (guild != null) - author = guild.GetUser(data.Author.Value.Id); - else - author = (channel as SocketChannel).GetUser(data.Author.Value.Id); - if (author == null) - author = SocketSimpleUser.Create(this, State, data.Author.Value); + var author = (guild != null ? guild.GetUser(data.Author.Value.Id) : (channel as SocketChannel).GetUser(data.Author.Value.Id)) ?? + SocketSimpleUser.Create(this, State, data.Author.Value); after = SocketMessage.Create(this, State, author, channel, data); } - if (before != null) - await _messageUpdatedEvent.InvokeAsync(before, after).ConfigureAwait(false); - else - await _messageUpdatedEvent.InvokeAsync(Optional.Create(), after).ConfigureAwait(false); + var cached = new Cached(data.Id, before, channel); + await _messageUpdatedEvent.InvokeAsync(cached, after).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_UPDATE referenced an unknown channel.").ConfigureAwait(false); - return; } } break; @@ -1324,15 +1310,13 @@ namespace Discord.WebSocket } var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); - if (msg != null) - await _messageDeletedEvent.InvokeAsync(data.Id, msg).ConfigureAwait(false); - else - await _messageDeletedEvent.InvokeAsync(data.Id, Optional.Create()).ConfigureAwait(false); + var cached = new Cached(data.Id, msg, channel, isDownloadable: false); + + await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_DELETE referenced an unknown channel.").ConfigureAwait(false); - return; } } break; @@ -1347,19 +1331,15 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); + var cached = new Cached(data.MessageId, cachedMsg, channel); - if (cachedMsg != null) - { - cachedMsg.AddReaction(reaction); - await _reactionAddedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); - return; - } - await _reactionAddedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + cachedMsg?.AddReaction(reaction); + + await _reactionAddedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_ADD referenced an unknown channel.").ConfigureAwait(false); - return; } break; } @@ -1374,18 +1354,15 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - if (cachedMsg != null) - { - cachedMsg.RemoveReaction(reaction); - await _reactionRemovedEvent.InvokeAsync(data.MessageId, cachedMsg, reaction).ConfigureAwait(false); - return; - } - await _reactionRemovedEvent.InvokeAsync(data.MessageId, Optional.Create(), reaction).ConfigureAwait(false); + var cached = new Cached(data.MessageId, cachedMsg, channel); + + cachedMsg?.RemoveReaction(reaction); + + await _reactionRemovedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); - return; } break; } @@ -1398,20 +1375,16 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - if (cachedMsg != null) - { - cachedMsg.ClearReactions(); - await _reactionsClearedEvent.InvokeAsync(data.MessageId, cachedMsg).ConfigureAwait(false); - return; - } - await _reactionsClearedEvent.InvokeAsync(data.MessageId, Optional.Create()); + var cached = new Cached(data.MessageId, cachedMsg, channel); + + cachedMsg?.ClearReactions(); + + await _reactionsClearedEvent.InvokeAsync(cached); } else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false); - return; } - break; } case "MESSAGE_DELETE_BULK": @@ -1431,16 +1404,13 @@ namespace Discord.WebSocket foreach (var id in data.Ids) { var msg = SocketChannelHelper.RemoveMessage(channel, this, id); - if (msg != null) - await _messageDeletedEvent.InvokeAsync(id, msg).ConfigureAwait(false); - else - await _messageDeletedEvent.InvokeAsync(id, Optional.Create()).ConfigureAwait(false); + var cached = new Cached(id, msg, channel, false); + await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); } } else { await _gatewayLogger.WarningAsync("MESSAGE_DELETE_BULK referenced an unknown channel.").ConfigureAwait(false); - return; } } break; @@ -1678,7 +1648,6 @@ namespace Discord.WebSocket catch (Exception ex) { await _gatewayLogger.ErrorAsync($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false); - return; } } From 8435186d79522377947f722912f540aae9e2654c Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Thu, 1 Dec 2016 18:37:53 +0100 Subject: [PATCH 213/263] returns. --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 2d510790f..f28d7961d 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1292,6 +1292,7 @@ namespace Discord.WebSocket else { await _gatewayLogger.WarningAsync("MESSAGE_UPDATE referenced an unknown channel.").ConfigureAwait(false); + return; } } break; @@ -1340,6 +1341,7 @@ namespace Discord.WebSocket else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_ADD referenced an unknown channel.").ConfigureAwait(false); + return; } break; } @@ -1384,6 +1386,7 @@ namespace Discord.WebSocket else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE_ALL referenced an unknown channel.").ConfigureAwait(false); + return; } break; } @@ -1648,6 +1651,7 @@ namespace Discord.WebSocket catch (Exception ex) { await _gatewayLogger.ErrorAsync($"Error handling {opCode}{(type != null ? $" ({type})" : "")}", ex).ConfigureAwait(false); + return; } } From 705d71875c69d68756e81ca6c8964ccf1fb81a47 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Fri, 16 Dec 2016 23:50:27 +0100 Subject: [PATCH 214/263] Simplified PR, renamed Cached to Cacheable. --- src/Discord.Net.Core/Utils/Cacheable.cs | 30 +++++++++++++ src/Discord.Net.Core/Utils/Cached.cs | 33 -------------- .../DiscordSocketClient.Events.cs | 20 ++++----- .../DiscordSocketClient.cs | 44 ++++++++++++------- 4 files changed, 67 insertions(+), 60 deletions(-) create mode 100644 src/Discord.Net.Core/Utils/Cacheable.cs delete mode 100644 src/Discord.Net.Core/Utils/Cached.cs diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs new file mode 100644 index 000000000..b85536434 --- /dev/null +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Discord +{ + public struct Cacheable + where TEntity : IEntity + where TId : IEquatable + { + public bool HasValue => !EqualityComparer.Default.Equals(Value, default(TEntity)); + public ulong Id { get; } + public TEntity Value { get; } + private Func> DownloadFunc { get; } + + internal Cacheable(TEntity value, ulong id, Func> downloadFunc) + { + Value = value; + Id = id; + DownloadFunc = downloadFunc; + } + + public async Task DownloadAsync() + { + return await DownloadFunc(); + } + + public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync(); + } +} \ No newline at end of file diff --git a/src/Discord.Net.Core/Utils/Cached.cs b/src/Discord.Net.Core/Utils/Cached.cs deleted file mode 100644 index 99c7794f7..000000000 --- a/src/Discord.Net.Core/Utils/Cached.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Discord.WebSocket; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Discord -{ - public struct Cached where T : IMessage - { - public bool IsCached => !EqualityComparer.Default.Equals(Value, default(T)); - public bool IsDownloadable { get; } - public ulong Id { get; } - public T Value { get; } - public ISocketMessageChannel Channel { get; } - - public Cached(ulong id, T value, ISocketMessageChannel channel, bool isDownloadable = true) - { - Id = id; - Value = value; - Channel = channel; - IsDownloadable = isDownloadable; - } - - public async Task DownloadAsync() - { - if (IsDownloadable) - return (T) await Channel.GetMessageAsync(Id); - throw new InvalidOperationException("This message cannot be downloaded."); - } - - public async Task GetOrDownloadAsync() => IsCached ? Value : await DownloadAsync(); - } -} diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 48f945b0c..63083bbb5 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -59,36 +59,36 @@ namespace Discord.WebSocket remove { _messageReceivedEvent.Remove(value); } } private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); - public event Func, Task> MessageDeleted + public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>(); - public event Func, SocketMessage, Task> MessageUpdated + private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); - public event Func, SocketReaction, Task> ReactionAdded + private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } remove { _reactionAddedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, SocketReaction, Task> ReactionRemoved + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved { add { _reactionRemovedEvent.Add(value); } remove { _reactionRemovedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, Task> ReactionsCleared + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, Task> ReactionsCleared { add { _reactionsClearedEvent.Add(value); } remove { _reactionsClearedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); + private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index f28d7961d..54ffb348e 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1265,7 +1265,7 @@ namespace Discord.WebSocket { var guild = (channel as SocketGuildChannel)?.Guild; if (guild != null && !guild.IsSynced) - { + { await _gatewayLogger.DebugAsync("Ignored MESSAGE_UPDATE, guild is not synced yet.").ConfigureAwait(false); return; } @@ -1281,13 +1281,20 @@ namespace Discord.WebSocket else if (data.Author.IsSpecified) { //Edited message isnt in cache, create a detached one - var author = (guild != null ? guild.GetUser(data.Author.Value.Id) : (channel as SocketChannel).GetUser(data.Author.Value.Id)) ?? - SocketSimpleUser.Create(this, State, data.Author.Value); + SocketUser author; + if (guild != null) + author = guild.GetUser(data.Author.Value.Id); + else + author = (channel as SocketChannel).GetUser(data.Author.Value.Id); + if (author == null) + author = SocketSimpleUser.Create(this, State, data.Author.Value); after = SocketMessage.Create(this, State, author, channel, data); } - var cached = new Cached(data.Id, before, channel); - await _messageUpdatedEvent.InvokeAsync(cached, after).ConfigureAwait(false); + if (before != null) + await _messageUpdatedEvent.InvokeAsync(before, after, channel).ConfigureAwait(false); + else + await _messageUpdatedEvent.InvokeAsync(Optional.Create(), after, channel).ConfigureAwait(false); } else { @@ -1299,25 +1306,26 @@ namespace Discord.WebSocket case "MESSAGE_DELETE": { await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE)").ConfigureAwait(false); - + var data = (payload as JToken).ToObject(_serializer); var channel = State.GetChannel(data.ChannelId) as ISocketMessageChannel; if (channel != null) { if (!((channel as SocketGuildChannel)?.Guild.IsSynced ?? true)) - { + { await _gatewayLogger.DebugAsync("Ignored MESSAGE_DELETE, guild is not synced yet.").ConfigureAwait(false); return; } var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); - var cached = new Cached(data.Id, msg, channel, isDownloadable: false); + var cacheable = new Cacheable(msg, data.Id, async () => await channel.GetMessageAsync(data.Id) as SocketUserMessage); - await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); + await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_DELETE referenced an unknown channel.").ConfigureAwait(false); + return; } } break; @@ -1332,11 +1340,11 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cached = new Cached(data.MessageId, cachedMsg, channel); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); cachedMsg?.AddReaction(reaction); - await _reactionAddedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); + await _reactionAddedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false); } else { @@ -1356,15 +1364,16 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cached = new Cached(data.MessageId, cachedMsg, channel); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); cachedMsg?.RemoveReaction(reaction); - await _reactionRemovedEvent.InvokeAsync(cached, reaction).ConfigureAwait(false); + await _reactionRemovedEvent.InvokeAsync(cacheable, channel, reaction).ConfigureAwait(false); } else { await _gatewayLogger.WarningAsync("MESSAGE_REACTION_REMOVE referenced an unknown channel.").ConfigureAwait(false); + return; } break; } @@ -1377,11 +1386,11 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - var cached = new Cached(data.MessageId, cachedMsg, channel); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); cachedMsg?.ClearReactions(); - await _reactionsClearedEvent.InvokeAsync(cached); + await _reactionsClearedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } else { @@ -1407,13 +1416,14 @@ namespace Discord.WebSocket foreach (var id in data.Ids) { var msg = SocketChannelHelper.RemoveMessage(channel, this, id); - var cached = new Cached(id, msg, channel, false); - await _messageDeletedEvent.InvokeAsync(cached).ConfigureAwait(false); + var cacheable = new Cacheable(msg, id, async () => await channel.GetMessageAsync(id) as SocketMessage); + await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } } else { await _gatewayLogger.WarningAsync("MESSAGE_DELETE_BULK referenced an unknown channel.").ConfigureAwait(false); + return; } } break; From daf0ac9347ff49026c3593791119f8c9b18fbe51 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 21 Dec 2016 20:52:07 -0500 Subject: [PATCH 215/263] Added docstrings for Cacheable --- src/Discord.Net.Core/Utils/Cacheable.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs index b85536434..ff63e4c69 100644 --- a/src/Discord.Net.Core/Utils/Cacheable.cs +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -4,12 +4,29 @@ using System.Threading.Tasks; namespace Discord { + /// + /// Contains an entity that may be cached. + /// + /// The type of entity that is cached + /// The type of this entity's ID public struct Cacheable where TEntity : IEntity where TId : IEquatable { + /// + /// Is this entity cached? + /// public bool HasValue => !EqualityComparer.Default.Equals(Value, default(TEntity)); + /// + /// The ID of this entity. + /// public ulong Id { get; } + /// + /// The entity, if it could be pulled from cache. + /// + /// + /// This value is not guaranteed to be set; in cases where the entity cannot be pulled from cache, it is null. + /// public TEntity Value { get; } private Func> DownloadFunc { get; } @@ -20,11 +37,19 @@ namespace Discord DownloadFunc = downloadFunc; } + /// + /// Downloads this entity to cache. + /// + /// An awaitable Task containing the downloaded entity. public async Task DownloadAsync() { return await DownloadFunc(); } + /// + /// Returns the cached entity if it exists; otherwise downloads it. + /// + /// An awaitable Task containing a cached or downloaded entity. public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync(); } } \ No newline at end of file From 2a1314da25dd0eede993a7fdf7fb85a032315d18 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Thu, 22 Dec 2016 03:41:23 +0100 Subject: [PATCH 216/263] Cleanup. --- .../DiscordSocketClient.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 54ffb348e..164fdeb07 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -108,11 +108,11 @@ namespace Discord.WebSocket { if (ex != null) { - await _gatewayLogger.WarningAsync("Connection Closed", ex).ConfigureAwait(false); + await _gatewayLogger.WarningAsync($"Connection Closed", ex).ConfigureAwait(false); await StartReconnectAsync(ex).ConfigureAwait(false); } else - await _gatewayLogger.WarningAsync("Connection Closed").ConfigureAwait(false); + await _gatewayLogger.WarningAsync($"Connection Closed").ConfigureAwait(false); }; LeftGuild += async g => await _gatewayLogger.InfoAsync($"Left {g.Name}").ConfigureAwait(false); @@ -408,7 +408,7 @@ namespace Discord.WebSocket /// public SocketUser GetUser(string username, string discriminator) { - return State.Users.FirstOrDefault(x => x.Discriminator == discriminator && x.Username == username); + return State.Users.Where(x => x.Discriminator == discriminator && x.Username == username).FirstOrDefault(); } internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model) { @@ -485,25 +485,25 @@ namespace Discord.WebSocket } } - public async Task SetStatusAsync(UserStatus status) + public async Task SetStatus(UserStatus status) { Status = status; if (status == UserStatus.AFK) _statusSince = DateTimeOffset.UtcNow; else _statusSince = null; - await SendStatusAsync().ConfigureAwait(false); + await SendStatus().ConfigureAwait(false); } - public async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) + public async Task SetGame(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) { if (name != null) Game = new Game(name, streamUrl, streamType); else Game = null; CurrentUser.Presence = new SocketPresence(Status, Game); - await SendStatusAsync().ConfigureAwait(false); + await SendStatus().ConfigureAwait(false); } - private async Task SendStatusAsync() + private async Task SendStatus() { var game = Game; var status = Status; @@ -770,7 +770,7 @@ namespace Discord.WebSocket if (data.Unavailable == true) { type = "GUILD_UNAVAILABLE"; - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false); var guild = State.GetGuild(data.Id); if (guild != null) @@ -780,13 +780,13 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.WarningAsync("GUILD_UNAVAILABLE referenced an unknown guild.").ConfigureAwait(false); + await _gatewayLogger.WarningAsync($"GUILD_UNAVAILABLE referenced an unknown guild.").ConfigureAwait(false); return; } } else { - await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); + await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_DELETE)").ConfigureAwait(false); _downloadUsersFor.TryRemove(data.Id); var guild = RemoveGuild(data.Id); @@ -797,7 +797,7 @@ namespace Discord.WebSocket } else { - await _gatewayLogger.WarningAsync("GUILD_DELETE referenced an unknown guild.").ConfigureAwait(false); + await _gatewayLogger.WarningAsync($"GUILD_DELETE referenced an unknown guild.").ConfigureAwait(false); return; } } @@ -810,7 +810,7 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_CREATE)").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); - SocketChannel channel; + SocketChannel channel = null; if (data.GuildId.IsSpecified) { var guild = State.GetGuild(data.GuildId.Value); @@ -867,7 +867,7 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received Dispatch (CHANNEL_DELETE)").ConfigureAwait(false); - SocketChannel channel; + SocketChannel channel = null; var data = (payload as JToken).ToObject(_serializer); if (data.GuildId.IsSpecified) { From 2d67cf36ae01e8fe9bf8badf2ecca3d8e60dcd07 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Sat, 24 Dec 2016 02:25:37 +0100 Subject: [PATCH 217/263] Changed Id from a ulong to generic TId, as per discussion. --- src/Discord.Net.Core/Utils/Cacheable.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs index ff63e4c69..48af580d9 100644 --- a/src/Discord.Net.Core/Utils/Cacheable.cs +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -20,7 +20,7 @@ namespace Discord /// /// The ID of this entity. /// - public ulong Id { get; } + public TId Id { get; } /// /// The entity, if it could be pulled from cache. /// @@ -30,7 +30,7 @@ namespace Discord public TEntity Value { get; } private Func> DownloadFunc { get; } - internal Cacheable(TEntity value, ulong id, Func> downloadFunc) + internal Cacheable(TEntity value, TId id, Func> downloadFunc) { Value = value; Id = id; From 4b13e3fb00c958fdecc288ed414dd121117a176f Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Sun, 25 Dec 2016 14:30:20 +0100 Subject: [PATCH 218/263] Changed MessageUpdated to use Cacheable as well, after discussion with Volt. --- src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs | 4 ++-- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 63083bbb5..03edeb7c0 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -65,12 +65,12 @@ namespace Discord.WebSocket remove { _messageDeletedEvent.Remove(value); } } private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); - public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated + public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); + private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 164fdeb07..3e3128354 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1291,10 +1291,9 @@ namespace Discord.WebSocket after = SocketMessage.Create(this, State, author, channel, data); } - if (before != null) - await _messageUpdatedEvent.InvokeAsync(before, after, channel).ConfigureAwait(false); - else - await _messageUpdatedEvent.InvokeAsync(Optional.Create(), after, channel).ConfigureAwait(false); + var cacheableBefore = new Cacheable(before, data.Id, async () => await channel.GetMessageAsync(data.Id) as SocketMessage); + + await _messageUpdatedEvent.InvokeAsync(cacheableBefore, after, channel).ConfigureAwait(false); } else { From 25547407c8e2f5214600d16e8d200d2f5cbac1e3 Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Thu, 2 Feb 2017 14:41:27 +0100 Subject: [PATCH 219/263] Now this should be ready to, I am the worst at git. --- .../DiscordShardedClient.Events.cs | 20 +++++++++---------- .../DiscordShardedClient.cs | 10 +++++----- .../DiscordSocketClient.cs | 15 +++++++------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs index 449d30599..f06bad926 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs @@ -33,36 +33,36 @@ namespace Discord.WebSocket remove { _messageReceivedEvent.Remove(value); } } private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); - public event Func, Task> MessageDeleted + public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _messageDeletedEvent = new AsyncEvent, Task>>(); - public event Func, SocketMessage, Task> MessageUpdated + private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, Task>>(); - public event Func, SocketReaction, Task> ReactionAdded + private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } remove { _reactionAddedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, SocketReaction, Task> ReactionRemoved + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved { add { _reactionRemovedEvent.Add(value); } remove { _reactionRemovedEvent.Remove(value); } } - private readonly AsyncEvent, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, SocketReaction, Task>>(); - public event Func, Task> ReactionsCleared + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, Task> ReactionsCleared { add { _reactionsClearedEvent.Add(value); } remove { _reactionsClearedEvent.Remove(value); } } - private readonly AsyncEvent, Task>> _reactionsClearedEvent = new AsyncEvent, Task>>(); + private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 832e35578..92e016d6e 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -303,11 +303,11 @@ namespace Discord.WebSocket client.ChannelUpdated += (oldChannel, newChannel) => _channelUpdatedEvent.InvokeAsync(oldChannel, newChannel); client.MessageReceived += (msg) => _messageReceivedEvent.InvokeAsync(msg); - client.MessageDeleted += (id, msg) => _messageDeletedEvent.InvokeAsync(id, msg); - client.MessageUpdated += (oldMsg, newMsg) => _messageUpdatedEvent.InvokeAsync(oldMsg, newMsg); - client.ReactionAdded += (id, msg, reaction) => _reactionAddedEvent.InvokeAsync(id, msg, reaction); - client.ReactionRemoved += (id, msg, reaction) => _reactionRemovedEvent.InvokeAsync(id, msg, reaction); - client.ReactionsCleared += (id, msg) => _reactionsClearedEvent.InvokeAsync(id, msg); + client.MessageDeleted += (cache, channel) => _messageDeletedEvent.InvokeAsync(cache, channel); + client.MessageUpdated += (oldMsg, newMsg, channel) => _messageUpdatedEvent.InvokeAsync(oldMsg, newMsg, channel); + client.ReactionAdded += (cache, channel, reaction) => _reactionAddedEvent.InvokeAsync(cache, channel, reaction); + client.ReactionRemoved += (cache, channel, reaction) => _reactionRemovedEvent.InvokeAsync(cache, channel, reaction); + client.ReactionsCleared += (cache, channel) => _reactionsClearedEvent.InvokeAsync(cache, channel); client.RoleCreated += (role) => _roleCreatedEvent.InvokeAsync(role); client.RoleDeleted += (role) => _roleDeletedEvent.InvokeAsync(role); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 3e3128354..a40ec04e8 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -485,25 +485,25 @@ namespace Discord.WebSocket } } - public async Task SetStatus(UserStatus status) + public async Task SetStatusAsync(UserStatus status) { Status = status; if (status == UserStatus.AFK) _statusSince = DateTimeOffset.UtcNow; else _statusSince = null; - await SendStatus().ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); } - public async Task SetGame(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) + public async Task SetGameAsync(string name, string streamUrl = null, StreamType streamType = StreamType.NotStreaming) { if (name != null) Game = new Game(name, streamUrl, streamType); else Game = null; CurrentUser.Presence = new SocketPresence(Status, Game); - await SendStatus().ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); } - private async Task SendStatus() + private async Task SendStatusAsync() { var game = Game; var status = Status; @@ -1205,8 +1205,9 @@ namespace Discord.WebSocket return; } - SocketUser user = State.GetUser(data.User.Id) ?? - (SocketUser) SocketSimpleUser.Create(this, State, data.User); + SocketUser user = State.GetUser(data.User.Id); + if (user == null) + user = SocketSimpleUser.Create(this, State, data.User); await _userUnbannedEvent.InvokeAsync(user, guild).ConfigureAwait(false); } else From d8682a82b2b0325425d34ef8b92ca19e64b7cb31 Mon Sep 17 00:00:00 2001 From: Sindre Langhus Date: Fri, 3 Feb 2017 15:04:54 +0100 Subject: [PATCH 220/263] Change all signatures in the SocketClients to interfaces. --- src/Discord.Net.Core/Utils/Cacheable.cs | 4 ++++ .../DiscordShardedClient.Events.cs | 21 ++++++++++--------- .../DiscordSocketClient.Events.cs | 20 +++++++++--------- .../DiscordSocketClient.cs | 12 +++++------ 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs index 48af580d9..da6c660b8 100644 --- a/src/Discord.Net.Core/Utils/Cacheable.cs +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -41,6 +41,8 @@ namespace Discord /// Downloads this entity to cache. /// /// An awaitable Task containing the downloaded entity. + /// Thrown when used from a user account. + /// Thrown when the message is deleted. public async Task DownloadAsync() { return await DownloadFunc(); @@ -50,6 +52,8 @@ namespace Discord /// Returns the cached entity if it exists; otherwise downloads it. /// /// An awaitable Task containing a cached or downloaded entity. + /// Thrown when used from a user account. + /// Thrown when the message is deleted and is not in cache. public async Task GetOrDownloadAsync() => HasValue ? Value : await DownloadAsync(); } } \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs index f06bad926..874062c56 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.Events.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using Discord.Net; namespace Discord.WebSocket { @@ -33,36 +34,36 @@ namespace Discord.WebSocket remove { _messageReceivedEvent.Remove(value); } } private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); - public event Func, ISocketMessageChannel, Task> MessageDeleted + public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); - public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated + private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); - public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded + private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } remove { _reactionAddedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); - public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved { add { _reactionRemovedEvent.Add(value); } remove { _reactionRemovedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); - public event Func, ISocketMessageChannel, Task> ReactionsCleared + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, Task> ReactionsCleared { add { _reactionsClearedEvent.Add(value); } remove { _reactionsClearedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs index 03edeb7c0..313e661f3 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.Events.cs @@ -59,36 +59,36 @@ namespace Discord.WebSocket remove { _messageReceivedEvent.Remove(value); } } private readonly AsyncEvent> _messageReceivedEvent = new AsyncEvent>(); - public event Func, ISocketMessageChannel, Task> MessageDeleted + public event Func, ISocketMessageChannel, Task> MessageDeleted { add { _messageDeletedEvent.Add(value); } remove { _messageDeletedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); - public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated + private readonly AsyncEvent, ISocketMessageChannel, Task>> _messageDeletedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + public event Func, SocketMessage, ISocketMessageChannel, Task> MessageUpdated { add { _messageUpdatedEvent.Add(value); } remove { _messageUpdatedEvent.Remove(value); } } - private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); - public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded + private readonly AsyncEvent, SocketMessage, ISocketMessageChannel, Task>> _messageUpdatedEvent = new AsyncEvent, SocketMessage, ISocketMessageChannel, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionAdded { add { _reactionAddedEvent.Add(value); } remove { _reactionAddedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); - public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionAddedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, SocketReaction, Task> ReactionRemoved { add { _reactionRemovedEvent.Add(value); } remove { _reactionRemovedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); - public event Func, ISocketMessageChannel, Task> ReactionsCleared + private readonly AsyncEvent, ISocketMessageChannel, SocketReaction, Task>> _reactionRemovedEvent = new AsyncEvent, ISocketMessageChannel, SocketReaction, Task>>(); + public event Func, ISocketMessageChannel, Task> ReactionsCleared { add { _reactionsClearedEvent.Add(value); } remove { _reactionsClearedEvent.Remove(value); } } - private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); + private readonly AsyncEvent, ISocketMessageChannel, Task>> _reactionsClearedEvent = new AsyncEvent, ISocketMessageChannel, Task>>(); //Roles public event Func RoleCreated diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index a40ec04e8..60ebeb535 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1292,7 +1292,7 @@ namespace Discord.WebSocket after = SocketMessage.Create(this, State, author, channel, data); } - var cacheableBefore = new Cacheable(before, data.Id, async () => await channel.GetMessageAsync(data.Id) as SocketMessage); + var cacheableBefore = new Cacheable(before, data.Id, async () => await channel.GetMessageAsync(data.Id)); await _messageUpdatedEvent.InvokeAsync(cacheableBefore, after, channel).ConfigureAwait(false); } @@ -1318,7 +1318,7 @@ namespace Discord.WebSocket } var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); - var cacheable = new Cacheable(msg, data.Id, async () => await channel.GetMessageAsync(data.Id) as SocketUserMessage); + var cacheable = new Cacheable(msg, data.Id, async () => await channel.GetMessageAsync(data.Id)); await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } @@ -1340,7 +1340,7 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.AddReaction(reaction); @@ -1364,7 +1364,7 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.RemoveReaction(reaction); @@ -1386,7 +1386,7 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as SocketUserMessage); + var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.ClearReactions(); @@ -1416,7 +1416,7 @@ namespace Discord.WebSocket foreach (var id in data.Ids) { var msg = SocketChannelHelper.RemoveMessage(channel, this, id); - var cacheable = new Cacheable(msg, id, async () => await channel.GetMessageAsync(id) as SocketMessage); + var cacheable = new Cacheable(msg, id, async () => await channel.GetMessageAsync(id)); await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } } From 858af5e8bbafc930a1ea5ff41d31db80c6067ea1 Mon Sep 17 00:00:00 2001 From: Aaron Scherer Date: Sat, 4 Feb 2017 05:48:48 -0800 Subject: [PATCH 221/263] Typo --- src/Discord.Net.Commands/Results/ParseResult.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Commands/Results/ParseResult.cs b/src/Discord.Net.Commands/Results/ParseResult.cs index 0f024cb44..d4a9af521 100644 --- a/src/Discord.Net.Commands/Results/ParseResult.cs +++ b/src/Discord.Net.Commands/Results/ParseResult.cs @@ -14,10 +14,10 @@ namespace Discord.Commands public bool IsSuccess => !Error.HasValue; - private ParseResult(IReadOnlyList argValues, IReadOnlyList paramValue, CommandError? error, string errorReason) + private ParseResult(IReadOnlyList argValues, IReadOnlyList paramValues, CommandError? error, string errorReason) { ArgValues = argValues; - ParamValues = paramValue; + ParamValues = paramValues; Error = error; ErrorReason = errorReason; } From d9fd0c34e412cf99320a56c2732c6cd0a221c6ea Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 7 Feb 2017 19:48:09 -0400 Subject: [PATCH 222/263] Fixed bugs with creating invites --- src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 4 ++++ src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs | 2 +- src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs | 2 +- .../Entities/Channels/SocketGuildChannel.cs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index b7d81c579..2036f8824 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -74,8 +74,12 @@ namespace Discord.Rest var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; if (maxAge.HasValue) args.MaxAge = maxAge.Value; + else + args.MaxAge = 0; if (maxUses.HasValue) args.MaxUses = maxUses.Value; + else + args.MaxUses = 0; var model = await client.ApiClient.CreateChannelInviteAsync(channel.Id, args, options).ConfigureAwait(false); return RestInviteMetadata.Create(client, null, channel, model); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index c11d76254..edb751133 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -118,7 +118,7 @@ namespace Discord.Rest public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); public override string ToString() => Name; diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 99fb3ecd5..05feac03e 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -51,7 +51,7 @@ namespace Discord.Rpc public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); public override string ToString() => Name; diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 207a60a4f..6a56c97fe 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -112,7 +112,7 @@ namespace Discord.WebSocket public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = true, RequestOptions options = null) + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); public new abstract SocketGuildUser GetUser(ulong id); From 0cf5493c618b7d0e08bd970707e19d0879bd3637 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 7 Feb 2017 21:10:30 -0400 Subject: [PATCH 223/263] Fixed crash, added DM/Group channel helpers --- src/Discord.Net.Core/IDiscordClient.cs | 2 ++ src/Discord.Net.Rest/BaseDiscordClient.cs | 4 ++++ src/Discord.Net.Rest/ClientHelper.cs | 18 +++++++++++++++-- src/Discord.Net.Rest/DiscordRestClient.cs | 20 ++++++++++++++++++- .../DiscordSocketClient.cs | 8 ++++++++ 5 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index a26f27dc0..b9a08d32d 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -17,6 +17,8 @@ namespace Discord Task GetChannelAsync(ulong id, CacheMode mode = CacheMode.AllowDownload); Task> GetPrivateChannelsAsync(CacheMode mode = CacheMode.AllowDownload); + Task> GetDMChannelsAsync(CacheMode mode = CacheMode.AllowDownload); + Task> GetGroupChannelsAsync(CacheMode mode = CacheMode.AllowDownload); Task> GetConnectionsAsync(); diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 592931c86..8948c87dc 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -134,6 +134,10 @@ namespace Discord.Rest => Task.FromResult(null); Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode) => Task.FromResult>(ImmutableArray.Create()); + Task> IDiscordClient.GetDMChannelsAsync(CacheMode mode) + => Task.FromResult>(ImmutableArray.Create()); + Task> IDiscordClient.GetGroupChannelsAsync(CacheMode mode) + => Task.FromResult>(ImmutableArray.Create()); Task> IDiscordClient.GetConnectionsAsync() => Task.FromResult>(ImmutableArray.Create()); diff --git a/src/Discord.Net.Rest/ClientHelper.cs b/src/Discord.Net.Rest/ClientHelper.cs index e7ef55033..456362be6 100644 --- a/src/Discord.Net.Rest/ClientHelper.cs +++ b/src/Discord.Net.Rest/ClientHelper.cs @@ -24,10 +24,24 @@ namespace Discord.Rest return RestChannel.Create(client, model); return null; } - public static async Task> GetPrivateChannelsAsync(BaseDiscordClient client) + public static async Task> GetPrivateChannelsAsync(BaseDiscordClient client) { var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); - return models.Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); + return models.Select(x => RestChannel.CreatePrivate(client, x)).ToImmutableArray(); + } + public static async Task> GetDMChannelsAsync(BaseDiscordClient client) + { + var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); + return models + .Where(x => x.Type == ChannelType.DM) + .Select(x => RestDMChannel.Create(client, x)).ToImmutableArray(); + } + public static async Task> GetGroupChannelsAsync(BaseDiscordClient client) + { + var models = await client.ApiClient.GetMyPrivateChannelsAsync().ConfigureAwait(false); + return models + .Where(x => x.Type == ChannelType.Group) + .Select(x => RestGroupChannel.Create(client, x)).ToImmutableArray(); } public static async Task> GetConnectionsAsync(BaseDiscordClient client) diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index f5f2cb8b0..0727576bf 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -39,8 +39,12 @@ namespace Discord.Rest public Task GetChannelAsync(ulong id) => ClientHelper.GetChannelAsync(this, id); /// - public Task> GetPrivateChannelsAsync() + public Task> GetPrivateChannelsAsync() => ClientHelper.GetPrivateChannelsAsync(this); + public Task> GetDMChannelsAsync() + => ClientHelper.GetDMChannelsAsync(this); + public Task> GetGroupChannelsAsync() + => ClientHelper.GetGroupChannelsAsync(this); /// public Task> GetConnectionsAsync() @@ -98,6 +102,20 @@ namespace Discord.Rest else return ImmutableArray.Create(); } + async Task> IDiscordClient.GetDMChannelsAsync(CacheMode mode) + { + if (mode == CacheMode.AllowDownload) + return await GetDMChannelsAsync().ConfigureAwait(false); + else + return ImmutableArray.Create(); + } + async Task> IDiscordClient.GetGroupChannelsAsync(CacheMode mode) + { + if (mode == CacheMode.AllowDownload) + return await GetGroupChannelsAsync().ConfigureAwait(false); + else + return ImmutableArray.Create(); + } async Task> IDiscordClient.GetConnectionsAsync() => await GetConnectionsAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 0641672d2..6a23d3e04 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -68,6 +68,10 @@ namespace Discord.WebSocket public new SocketSelfUser CurrentUser { get { return base.CurrentUser as SocketSelfUser; } private set { base.CurrentUser = value; } } public IReadOnlyCollection Guilds => State.Guilds; public IReadOnlyCollection PrivateChannels => State.PrivateChannels; + public IReadOnlyCollection DMChannels + => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); + public IReadOnlyCollection GroupChannels + => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); public IReadOnlyCollection VoiceRegions => _voiceRegions.ToReadOnlyCollection(); /// Creates a new REST/WebSocket discord client. @@ -1803,6 +1807,10 @@ namespace Discord.WebSocket => Task.FromResult(GetChannel(id)); Task> IDiscordClient.GetPrivateChannelsAsync(CacheMode mode) => Task.FromResult>(PrivateChannels); + Task> IDiscordClient.GetDMChannelsAsync(CacheMode mode) + => Task.FromResult>(DMChannels); + Task> IDiscordClient.GetGroupChannelsAsync(CacheMode mode) + => Task.FromResult>(GroupChannels); async Task> IDiscordClient.GetConnectionsAsync() => await GetConnectionsAsync().ConfigureAwait(false); From ea3a8f6a01a5a1bb553af6c7fd869b8dc6c72b94 Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 7 Feb 2017 21:13:26 -0400 Subject: [PATCH 224/263] Added EmbedBuilder.AddField(EmbedFieldBuilder) --- src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs index 2f5e954be..d798ec60d 100644 --- a/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs @@ -92,6 +92,11 @@ namespace Discord return this; } + public EmbedBuilder AddField(EmbedFieldBuilder field) + { + _fields.Add(field); + return this; + } public EmbedBuilder AddField(Action action) { var field = new EmbedFieldBuilder(); From 8be4cb72e364f542f18ca711e6401fc795b497ca Mon Sep 17 00:00:00 2001 From: RogueException Date: Tue, 7 Feb 2017 21:15:27 -0400 Subject: [PATCH 225/263] SocketSystemMessage Internal -> Public --- .../Entities/Messages/SocketSystemMessage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs index 7678bb412..50cdb964b 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketSystemMessage.cs @@ -4,7 +4,7 @@ using Model = Discord.API.Message; namespace Discord.WebSocket { [DebuggerDisplay(@"{DebuggerDisplay,nq}")] - internal class SocketSystemMessage : SocketMessage, ISystemMessage + public class SocketSystemMessage : SocketMessage, ISystemMessage { public MessageType Type { get; private set; } From a551064eaf9712b3e7ba6eb2a6c6434d4ad25124 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 10 Feb 2017 15:44:24 +0000 Subject: [PATCH 226/263] Add IDependencyMap injection for public properties --- .../Utilities/ReflectionUtils.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 27ea601bf..635f00fc3 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -19,6 +19,7 @@ namespace Discord.Commands var constructor = constructors[0]; System.Reflection.ParameterInfo[] parameters = constructor.GetParameters(); + System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties.Where(p => p.CanWrite).ToArray(); return (map) => { @@ -40,15 +41,33 @@ namespace Discord.Commands args[i] = arg; } + T obj; try { - return (T)constructor.Invoke(args); + obj = (T)constructor.Invoke(args); } catch (Exception ex) { throw new Exception($"Failed to create \"{typeInfo.FullName}\"", ex); } + + foreach(var property in properties) + { + object prop; + if (map == null || !map.TryGet(property.PropertyType, out prop)) + { + if (property.PropertyType == typeof(CommandService)) + prop = service; + else if (property.PropertyType == typeof(IDependencyMap)) + prop = map; + else + throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\", dependency \"{property.PropertyType.Name}\" was not found."); + } + property.SetValue(prop, null); + } + return obj; }; } + } } From f0b4c24e8232be8cd478d4fd67ca90468815eb56 Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 10 Feb 2017 21:52:33 +0000 Subject: [PATCH 227/263] Add InjectAttribute for annotating injectable properties --- src/Discord.Net.Commands/Attributes/InjectAttribute.cs | 9 +++++++++ src/Discord.Net.Commands/Utilities/ReflectionUtils.cs | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/Discord.Net.Commands/Attributes/InjectAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs new file mode 100644 index 000000000..b970fa8c8 --- /dev/null +++ b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Discord.Commands { + + [AttributeUsage(AttributeTargets.Property)] + public class InjectAttribute : Attribute { + } + +} diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 635f00fc3..5bed627df 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -19,7 +19,9 @@ namespace Discord.Commands var constructor = constructors[0]; System.Reflection.ParameterInfo[] parameters = constructor.GetParameters(); - System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties.Where(p => p.CanWrite).ToArray(); + System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties + .Where(p => p.CanWrite && p.GetCustomAttribute() != null) + .ToArray(); return (map) => { From f1df412341b926ddd17dffa506db717fd33c690a Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 10 Feb 2017 22:02:18 +0000 Subject: [PATCH 228/263] Change whitelist injection into blacklist injection --- .../Attributes/{InjectAttribute.cs => DontInjectAttribute.cs} | 2 +- src/Discord.Net.Commands/Utilities/ReflectionUtils.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/Discord.Net.Commands/Attributes/{InjectAttribute.cs => DontInjectAttribute.cs} (66%) diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/DontInjectAttribute.cs similarity index 66% rename from src/Discord.Net.Commands/Attributes/InjectAttribute.cs rename to src/Discord.Net.Commands/Attributes/DontInjectAttribute.cs index b970fa8c8..bd966e129 100644 --- a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/DontInjectAttribute.cs @@ -3,7 +3,7 @@ using System; namespace Discord.Commands { [AttributeUsage(AttributeTargets.Property)] - public class InjectAttribute : Attribute { + public class DontInjectAttribute : Attribute { } } diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 5bed627df..14192e0bd 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -20,7 +20,7 @@ namespace Discord.Commands var constructor = constructors[0]; System.Reflection.ParameterInfo[] parameters = constructor.GetParameters(); System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties - .Where(p => p.CanWrite && p.GetCustomAttribute() != null) + .Where(p => p.CanWrite && p.GetCustomAttribute() == null) .ToArray(); return (map) => From bb9568607839c89cb4f2c61215fb3c62416b8f4f Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 10 Feb 2017 22:11:06 +0000 Subject: [PATCH 229/263] Update docs to reflect the change. --- docs/guides/commands.md | 199 ++++++++++++----------- docs/guides/samples/dependency_module.cs | 20 ++- 2 files changed, 120 insertions(+), 99 deletions(-) diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 4c5100946..71fdc58e9 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -5,16 +5,16 @@ ## Setup -To use Commands, you must create a [Commands Service] and a +To use Commands, you must create a [Commands Service] and a Command Handler. -Included below is a very bare-bones Command Handler. You can extend -your Command Handler as much as you like, however the below is the +Included below is a very bare-bones Command Handler. You can extend +your Command Handler as much as you like, however the below is the bare minimum. -The CommandService optionally will accept a [CommandServiceConfig], -which _does_ set a few default values for you. It is recommended to -look over the properties in [CommandServiceConfig], and their default +The CommandService optionally will accept a [CommandServiceConfig], +which _does_ set a few default values for you. It is recommended to +look over the properties in [CommandServiceConfig], and their default values. [!code-csharp[Command Handler](samples/command_handler.cs)] @@ -22,38 +22,38 @@ values. [Command Service]: xref:Discord.Commands.CommandService [CommandServiceConfig]: xref:Discord.Commands.CommandServiceConfig -## With Attributes +## With Attributes -In 1.0, Commands can be defined ahead of time, with attributes, or +In 1.0, Commands can be defined ahead of time, with attributes, or at runtime, with builders. -For most bots, ahead-of-time commands should be all you need, and this +For most bots, ahead-of-time commands should be all you need, and this is the recommended method of defining commands. -### Modules +### Modules The first step to creating commands is to create a _module_. -Modules are an organizational pattern that allow you to write your -commands in different classes, and have them automatically loaded. +Modules are an organizational pattern that allow you to write your +commands in different classes, and have them automatically loaded. -Discord.Net's implementation of Modules is influenced heavily from -ASP.Net Core's Controller pattern. This means that the lifetime of a +Discord.Net's implementation of Modules is influenced heavily from +ASP.Net Core's Controller pattern. This means that the lifetime of a module instance is only as long as the command being invoked. -**Avoid using long-running code** in your modules whereever possible. -You should **not** be implementing very much logic into your modules; +**Avoid using long-running code** in your modules whereever possible. +You should **not** be implementing very much logic into your modules; outsource to a service for that. -If you are unfamiliar with Inversion of Control, it is recommended to -read the MSDN article on [IoC] and [Dependency Injection]. +If you are unfamiliar with Inversion of Control, it is recommended to +read the MSDN article on [IoC] and [Dependency Injection]. -To begin, create a new class somewhere in your project, and +To begin, create a new class somewhere in your project, and inherit the class from [ModuleBase]. This class **must** be `public`. >[!NOTE] ->[ModuleBase] is an _abstract_ class, meaning that you may extend it ->or override it as you see fit. Your module may inherit from any +>[ModuleBase] is an _abstract_ class, meaning that you may extend it +>or override it as you see fit. Your module may inherit from any >extension of ModuleBase. By now, your module should look like this: @@ -63,36 +63,36 @@ By now, your module should look like this: [Dependency Injection]: https://msdn.microsoft.com/en-us/library/ff921152.aspx [ModuleBase]: xref:Discord.Commands.ModuleBase`1 -### Adding Commands +### Adding Commands -The next step to creating commands, is actually creating commands. +The next step to creating commands, is actually creating commands. -To create a command, add a method to your module of type `Task`. -Typically, you will want to mark this method as `async`, although it is -not required. +To create a command, add a method to your module of type `Task`. +Typically, you will want to mark this method as `async`, although it is +not required. -Adding parameters to a command is done by adding parameters to the +Adding parameters to a command is done by adding parameters to the parent Task. -For example, to take an integer as an argument, add `int arg`. To take -a user as an argument, add `IUser user`. In 1.0, a command can accept -nearly any type of argument; a full list of types that are parsed by +For example, to take an integer as an argument, add `int arg`. To take +a user as an argument, add `IUser user`. In 1.0, a command can accept +nearly any type of argument; a full list of types that are parsed by default can be found in the below section on _Type Readers_. -Parameters, by default, are always required. To make a parameter -optional, give it a default value. To accept a comma-separated list, -set the parameter to `params Type[]`. +Parameters, by default, are always required. To make a parameter +optional, give it a default value. To accept a comma-separated list, +set the parameter to `params Type[]`. -Should a parameter include spaces, it **must** be wrapped in quotes. -For example, for a command with a parameter `string food`, you would -execute it with `!favoritefood "Key Lime Pie"`. +Should a parameter include spaces, it **must** be wrapped in quotes. +For example, for a command with a parameter `string food`, you would +execute it with `!favoritefood "Key Lime Pie"`. -If you would like a parameter to parse until the end of a command, -flag the parameter with the [RemainderAttribute]. This will allow a +If you would like a parameter to parse until the end of a command, +flag the parameter with the [RemainderAttribute]. This will allow a user to invoke a command without wrapping a parameter in quotes. -Finally, flag your command with the [CommandAttribute]. (You must -specify a name for this command, except for when it is part of a +Finally, flag your command with the [CommandAttribute]. (You must +specify a name for this command, except for when it is part of a module group - see below). [RemainderAttribute]: xref:Discord.Commands.RemainderAttribute @@ -100,52 +100,52 @@ module group - see below). ### Command Overloads -You may add overloads of your commands, and the command parser will +You may add overloads of your commands, and the command parser will automatically pick up on it. -If, for whatever reason, you have too commands which are ambiguous to -each other, you may use the @Discord.Commands.PriorityAttribute to -specify which should be tested before the other. +If, for whatever reason, you have too commands which are ambiguous to +each other, you may use the @Discord.Commands.PriorityAttribute to +specify which should be tested before the other. -Priority's are sorted in ascending order; the higher priority will be +Priority's are sorted in ascending order; the higher priority will be called first. -### CommandContext +### CommandContext -Every command can access the execution context through the [Context] -property on [ModuleBase]. CommandContext allows you to access the -message, channel, guild, and user that the command was invoked from, -as well as the underlying discord client the command was invoked from. +Every command can access the execution context through the [Context] +property on [ModuleBase]. CommandContext allows you to access the +message, channel, guild, and user that the command was invoked from, +as well as the underlying discord client the command was invoked from. -Different types of Contexts may be specified using the generic variant -of [ModuleBase]. When using a [SocketCommandContext], for example, -the properties on this context will already be Socket entities. You +Different types of Contexts may be specified using the generic variant +of [ModuleBase]. When using a [SocketCommandContext], for example, +the properties on this context will already be Socket entities. You will not need to cast them. -To reply to messages, you may also invoke [ReplyAsync], instead of +To reply to messages, you may also invoke [ReplyAsync], instead of accessing the channel through the [Context] and sending a message. [Context]: xref:Discord.Commands.ModuleBase`1#Discord_Commands_ModuleBase_1_Context [SocketCommandContext]: xref:Discord.Commands.SocketCommandContext >![WARNING] ->Contexts should **NOT** be mixed! You cannot have one module that +>Contexts should **NOT** be mixed! You cannot have one module that >uses CommandContext, and another that uses SocketCommandContext. -### Example Module +### Example Module At this point, your module should look comparable to this example: [!code-csharp[Example Module](samples/module.cs)] #### Loading Modules Automatically -The Command Service can automatically discover all classes in an +The Command Service can automatically discover all classes in an Assembly that inherit [ModuleBase], and load them. -To opt a module out of auto-loading, flag it with +To opt a module out of auto-loading, flag it with [DontAutoLoadAttribute] -Invoke [CommandService.AddModulesAsync] to discover modules and +Invoke [CommandService.AddModulesAsync] to discover modules and install them. [DontAutoLoadAttribute]: xref:Discord.Commands.DontAutoLoadAttribute @@ -153,33 +153,38 @@ install them. #### Loading Modules Manually -To manually load a module, invoke [CommandService.AddModuleAsync], -by passing in the generic type of your module, and optionally +To manually load a module, invoke [CommandService.AddModuleAsync], +by passing in the generic type of your module, and optionally a dependency map. [CommandService.AddModuleAsync]: xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddModuleAsync__1 ### Module Constructors -Modules are constructed using Dependency Injection. Any parameters -that are placed in the constructor must be injected into an -@Discord.Commands.IDependencyMap. Alternatively, you may accept an +Modules are constructed using Dependency Injection. Any parameters +that are placed in the constructor must be injected into an +@Discord.Commands.IDependencyMap. Alternatively, you may accept an IDependencyMap as an argument and extract services yourself. +### Module Properties + +Modules with public settable properties will be have them injected after +construction. + ### Module Groups -Module Groups allow you to create a module where commands are prefixed. -To create a group, flag a module with the +Module Groups allow you to create a module where commands are prefixed. +To create a group, flag a module with the @Discord.Commands.GroupAttribute -Module groups also allow you to create **nameless commands**, where the -[CommandAttribute] is configured with no name. In this case, the +Module groups also allow you to create **nameless commands**, where the +[CommandAttribute] is configured with no name. In this case, the command will inherit the name of the group it belongs to. ### Submodules -Submodules are modules that reside within another module. Typically, -submodules are used to create nested groups (although not required to +Submodules are modules that reside within another module. Typically, +submodules are used to create nested groups (although not required to create nested groups). [!code-csharp[Groups and Submodules](samples/groups.cs)] @@ -190,40 +195,46 @@ create nested groups). ## Dependency Injection -The commands service is bundled with a very barebones Dependency -Injection service for your convienence. It is recommended that +The commands service is bundled with a very barebones Dependency +Injection service for your convienence. It is recommended that you use DI when writing your modules. ### Setup -First, you need to create an @Discord.Commands.IDependencyMap. -The library includes @Discord.Commands.DependencyMap to help with -this, however you may create your own IDependencyMap if you wish. +First, you need to create an @Discord.Commands.IDependencyMap. +The library includes @Discord.Commands.DependencyMap to help with +this, however you may create your own IDependencyMap if you wish. -Next, add the dependencies your modules will use to the map. +Next, add the dependencies your modules will use to the map. -Finally, pass the map into the `LoadAssembly` method. +Finally, pass the map into the `LoadAssembly` method. Your modules will automatically be loaded with this dependency map. [!code-csharp[DependencyMap Setup](samples/dependency_map_setup.cs)] ### Usage in Modules -In the constructor of your module, any parameters will be filled in by +In the constructor of your module, any parameters will be filled in by the @Discord.Commands.IDependencyMap you pass into `LoadAssembly`. +Any publicly settable properties will also be filled in the same manner. + +>[!NOTE] +> Annotating a property with the [DontInject] attribute will prevent it from +being injected. + >[!NOTE] ->If you accept `CommandService` or `IDependencyMap` as a parameter in -your constructor, these parameters will be filled by the -CommandService the module was loaded from, and the DependencyMap passed -into it, respectively. +>If you accept `CommandService` or `IDependencyMap` as a parameter in +your constructor or as an injectable property, these entries will be filled +by the CommandService the module was loaded from, and the DependencyMap passed +into it, respectively. [!code-csharp[DependencyMap in Modules](samples/dependency_module.cs)] # Preconditions -Preconditions serve as a permissions system for your commands. Keep in -mind, however, that they are not limited to _just_ permissions, and +Preconditions serve as a permissions system for your commands. Keep in +mind, however, that they are not limited to _just_ permissions, and can be as complex as you want them to be. >[!NOTE] @@ -231,7 +242,7 @@ can be as complex as you want them to be. ## Bundled Preconditions -Commands ships with four bundled preconditions; you may view their +Commands ships with four bundled preconditions; you may view their usages on their API page. - @Discord.Commands.RequireContextAttribute @@ -244,13 +255,13 @@ usages on their API page. To write your own preconditions, create a new class that inherits from @Discord.Commands.PreconditionAttribute -In order for your precondition to function, you will need to override +In order for your precondition to function, you will need to override [CheckPermissions]. Your IDE should provide an option to fill this in for you. -Return [PreconditionResult.FromSuccess] if the context met the -required parameters, otherwise return [PreconditionResult.FromError], +Return [PreconditionResult.FromSuccess] if the context met the +required parameters, otherwise return [PreconditionResult.FromError], optionally including an error message. [!code-csharp[Custom Precondition](samples/require_owner.cs)] @@ -261,7 +272,7 @@ optionally including an error message. # Type Readers -Type Readers allow you to parse different types of arguments in +Type Readers allow you to parse different types of arguments in your commands. By default, the following Types are supported arguments: @@ -282,19 +293,19 @@ By default, the following Types are supported arguments: ### Creating a Type Readers -To create a TypeReader, create a new class that imports @Discord and +To create a TypeReader, create a new class that imports @Discord and @Discord.Commands. Ensure your class inherits from @Discord.Commands.TypeReader Next, satisfy the `TypeReader` class by overriding [Read]. >[!NOTE] ->In many cases, Visual Studio can fill this in for you, using the +>In many cases, Visual Studio can fill this in for you, using the >"Implement Abstract Class" IntelliSense hint. Inside this task, add whatever logic you need to parse the input string. -Finally, return a `TypeReaderResult`. If you were able to successfully -parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. +Finally, return a `TypeReaderResult`. If you were able to successfully +parse the input, return `TypeReaderResult.FromSuccess(parsedInput)`. Otherwise, return `TypeReaderResult.FromError`. [Read]: xref:Discord.Commands.TypeReader#Discord_Commands_TypeReader_Read_Discord_Commands_CommandContext_System_String_ @@ -305,5 +316,5 @@ Otherwise, return `TypeReaderResult.FromError`. ### Installing TypeReaders -TypeReaders are not automatically discovered by the Command Service, +TypeReaders are not automatically discovered by the Command Service, and must be explicitly added. To install a TypeReader, invoke [CommandService.AddTypeReader](xref:Discord.Commands.CommandService#Discord_Commands_CommandService_AddTypeReader__1_Discord_Commands_TypeReader_). diff --git a/docs/guides/samples/dependency_module.cs b/docs/guides/samples/dependency_module.cs index 2e1d662f8..0d65a1eea 100644 --- a/docs/guides/samples/dependency_module.cs +++ b/docs/guides/samples/dependency_module.cs @@ -6,6 +6,7 @@ public class ModuleA : ModuleBase { private readonly DatabaseService _database; + // Dependencies can be injected via the constructor public ModuleA(DatabaseService database) { _database = database; @@ -20,12 +21,21 @@ public class ModuleA : ModuleBase public class ModuleB { - private CommandService _commands; - private NotificationService _notification; - + + // Public settable properties will be injected + public AnnounceService { get; set; } + + // Public properties without setters will not + public CommandService Commands { get; } + + // Public properties annotated with [DontInject] will not + [DontInject] + public NotificationService { get; set; } + public ModuleB(CommandService commands, IDependencyMap map) { - _commands = commands; + Commands = commands; _notification = map.Get(); } -} \ No newline at end of file + +} From 145ae1518b9343ba37edad93b44e94739e193778 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 11 Feb 2017 05:48:45 +0000 Subject: [PATCH 230/263] Fix properties not being set properly on injection --- src/Discord.Net.Commands/Utilities/ReflectionUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 14192e0bd..e569401bc 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -65,7 +65,7 @@ namespace Discord.Commands else throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\", dependency \"{property.PropertyType.Name}\" was not found."); } - property.SetValue(prop, null); + property.SetValue(obj, prop); } return obj; }; From e0a0d2100d0f728dacb732c230071d3582e78d3b Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 11 Feb 2017 01:49:28 -0400 Subject: [PATCH 231/263] Reduced missed heartbeat aggressiveness --- .../DiscordSocketClient.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 6a23d3e04..fa6995c27 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -28,6 +28,7 @@ namespace Discord.WebSocket private readonly JsonSerializer _serializer; private readonly SemaphoreSlim _connectionGroupLock; private readonly DiscordSocketClient _parentClient; + private readonly ConcurrentQueue _heartbeatTimes; private string _sessionId; private int _lastSeq; @@ -35,9 +36,8 @@ namespace Discord.WebSocket private TaskCompletionSource _connectTask; private CancellationTokenSource _cancelToken, _reconnectCancelToken; private Task _heartbeatTask, _guildDownloadTask, _reconnectTask; - private long _heartbeatTime; private int _unavailableGuilds; - private long _lastGuildAvailableTime; + private long _lastGuildAvailableTime, _lastMessageTime; private int _nextAudioId; private bool _canReconnect; private DateTimeOffset? _statusSince; @@ -93,6 +93,7 @@ namespace Discord.WebSocket ConnectionTimeout = config.ConnectionTimeout; State = new ClientState(0, 0); _downloadUsersFor = new ConcurrentHashSet(); + _heartbeatTimes = new ConcurrentQueue(); _nextAudioId = 1; _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); @@ -292,6 +293,10 @@ namespace Discord.WebSocket await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; + long times; + while (_heartbeatTimes.TryDequeue(out times)) { } + _lastMessageTime = 0; + await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); var guildDownloadTask = _guildDownloadTask; if (guildDownloadTask != null) @@ -538,6 +543,8 @@ namespace Discord.WebSocket { if (seq != null) _lastSeq = seq.Value; + _lastMessageTime = Environment.TickCount; + try { switch (opCode) @@ -547,7 +554,6 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); - _heartbeatTime = 0; _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, _gatewayLogger); } break; @@ -562,12 +568,10 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); - var heartbeatTime = _heartbeatTime; - if (heartbeatTime != 0) + long time; + if (_heartbeatTimes.TryDequeue(out time)) { - int latency = (int)(Environment.TickCount - _heartbeatTime); - _heartbeatTime = 0; - + int latency = (int)(Environment.TickCount - time); int before = Latency; Latency = latency; @@ -1693,7 +1697,10 @@ namespace Discord.WebSocket await logger.DebugAsync("Heartbeat Started").ConfigureAwait(false); while (!cancelToken.IsCancellationRequested) { - if (_heartbeatTime != 0) //Server never responded to our last heartbeat + var now = Environment.TickCount; + + //Did server respond to our last heartbeat, or are we still receiving messages (long load?) + if (_heartbeatTimes.Count != 0 && (now - _lastMessageTime) > intervalMillis) { if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) { @@ -1702,7 +1709,7 @@ namespace Discord.WebSocket return; } } - _heartbeatTime = Environment.TickCount; + _heartbeatTimes.Enqueue(now); try { From 40ede62e4d635081b207cd44108caa68db3e7e04 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 11 Feb 2017 13:38:26 -0500 Subject: [PATCH 232/263] Remove Auto-Injection this should be handled by #520 --- .../Attributes/InjectAttribute.cs | 15 --------------- .../Utilities/ReflectionUtils.cs | 15 --------------- 2 files changed, 30 deletions(-) delete mode 100644 src/Discord.Net.Commands/Attributes/InjectAttribute.cs diff --git a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs b/src/Discord.Net.Commands/Attributes/InjectAttribute.cs deleted file mode 100644 index 09c0b553b..000000000 --- a/src/Discord.Net.Commands/Attributes/InjectAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Discord.Commands -{ - /// - /// Indicates that this property should be filled in by dependency injection. - /// - /// - /// This property **MUST** have a setter. - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class InjectAttribute : Attribute - { - } -} diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 0bcb24882..1923734e7 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -43,21 +43,6 @@ namespace Discord.Commands try { T instance = (T)constructor.Invoke(args); - var fields = instance.GetType().GetRuntimeProperties().Where(p => p.GetCustomAttribute() != null).Where(p => p.CanWrite); - foreach (var field in fields) - { - object arg; - if (map == null || !map.TryGet(field.PropertyType, out arg)) - { - if (field.PropertyType == typeof(CommandService)) - arg = service; - else if (field.PropertyType == typeof(IDependencyMap)) - arg = map; - else - throw new InvalidOperationException($"Failed to inject \"{typeInfo.FullName}\", dependency \"{field.PropertyType.FullName}\" was not found."); - } - field.SetValue(instance, arg); - } return instance; } catch (Exception ex) From 6352cbebeff0f686098176eb477f8509da3cf246 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 11 Feb 2017 13:53:14 -0500 Subject: [PATCH 233/263] Add TryAdd to DependencyMaps --- .../Dependencies/DependencyMap.cs | 18 +++++++++++++ .../Dependencies/IDependencyMap.cs | 27 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs index 0761f5fff..f5adf1a8c 100644 --- a/src/Discord.Net.Commands/Dependencies/DependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/DependencyMap.cs @@ -18,12 +18,21 @@ namespace Discord.Commands public void Add(T obj) where T : class => AddFactory(() => obj); /// + public bool TryAdd(T obj) where T : class + => TryAddFactory(() => obj); + /// public void AddTransient() where T : class, new() => AddFactory(() => new T()); /// + public bool TryAddTransient() where T : class, new() + => TryAddFactory(() => new T()); + /// public void AddTransient() where TKey : class where TImpl : class, TKey, new() => AddFactory(() => new TImpl()); + public bool TryAddTransient() where TKey : class + where TImpl : class, TKey, new() + => TryAddFactory(() => new TImpl()); /// public void AddFactory(Func factory) where T : class @@ -33,6 +42,15 @@ namespace Discord.Commands throw new InvalidOperationException($"The dependency map already contains \"{t.FullName}\""); map.Add(t, factory); } + /// + public bool TryAddFactory(Func factory) where T : class + { + var t = typeof(T); + if (map.ContainsKey(t)) + return false; + map.Add(t, factory); + return true; + } /// public T Get() diff --git a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs index fb042c304..a55a9e4c5 100644 --- a/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs +++ b/src/Discord.Net.Commands/Dependencies/IDependencyMap.cs @@ -11,11 +11,24 @@ namespace Discord.Commands /// The instance of a service. void Add(T obj) where T : class; /// + /// Tries to add an instance of a service to be injected. + /// + /// The type of service. + /// The instance of a service. + /// A bool, indicating if the service was successfully added to the DependencyMap. + bool TryAdd(T obj) where T : class; + /// /// Add a service that will be injected by a new instance every time. /// /// The type of instance to inject. void AddTransient() where T : class, new(); /// + /// Tries to add a service that will be injected by a new instance every time. + /// + /// The type of instance to inject. + /// A bool, indicating if the service was successfully added to the DependencyMap. + bool TryAddTransient() where T : class, new(); + /// /// Add a service that will be injected by a new instance every time. /// /// The type to look for when injecting. @@ -25,11 +38,25 @@ namespace Discord.Commands /// void AddTransient() where TKey: class where TImpl : class, TKey, new(); /// + /// Tries to add a service that will be injected by a new instance every time. + /// + /// The type to look for when injecting. + /// The type to inject when injecting. + /// A bool, indicating if the service was successfully added to the DependencyMap. + bool TryAddTransient() where TKey : class where TImpl : class, TKey, new(); + /// /// Add a service that will be injected by a factory. /// /// The type to look for when injecting. /// The factory that returns a type of this service. void AddFactory(Func factory) where T : class; + /// + /// Tries to add a service that will be injected by a factory. + /// + /// The type to look for when injecting. + /// The factory that returns a type of this service. + /// A bool, indicating if the service was successfully added to the DependencyMap. + bool TryAddFactory(Func factory) where T : class; /// /// Pull an object from the map. From 73f00eb0d7ad8da5d4ae80549388b6477f350fd6 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 11 Feb 2017 20:12:12 +0000 Subject: [PATCH 234/263] Ensure injected properties have public setters --- src/Discord.Net.Commands/Utilities/ReflectionUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index e569401bc..230b30312 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -20,7 +20,7 @@ namespace Discord.Commands var constructor = constructors[0]; System.Reflection.ParameterInfo[] parameters = constructor.GetParameters(); System.Reflection.PropertyInfo[] properties = typeInfo.DeclaredProperties - .Where(p => p.CanWrite && p.GetCustomAttribute() == null) + .Where(p => p.SetMethod?.IsPublic == true && p.GetCustomAttribute() == null) .ToArray(); return (map) => From 9609520a53b3f91d722b68bbe189cc419f2491b9 Mon Sep 17 00:00:00 2001 From: james7132 Date: Sat, 11 Feb 2017 20:12:53 +0000 Subject: [PATCH 235/263] Remove "Get" statement from example docs --- docs/guides/samples/dependency_module.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/guides/samples/dependency_module.cs b/docs/guides/samples/dependency_module.cs index 0d65a1eea..561b0f6ac 100644 --- a/docs/guides/samples/dependency_module.cs +++ b/docs/guides/samples/dependency_module.cs @@ -32,10 +32,9 @@ public class ModuleB [DontInject] public NotificationService { get; set; } - public ModuleB(CommandService commands, IDependencyMap map) + public ModuleB(CommandService commands) { Commands = commands; - _notification = map.Get(); } } From 70e8cea6e29b3e7f8c898b408ac1c49fac670dcf Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 11 Feb 2017 23:15:48 -0400 Subject: [PATCH 236/263] Fixed order for permission resolving --- src/Discord.Net.Core/Utils/Permissions.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Utils/Permissions.cs b/src/Discord.Net.Core/Utils/Permissions.cs index 390c142de..a6c545da0 100644 --- a/src/Discord.Net.Core/Utils/Permissions.cs +++ b/src/Discord.Net.Core/Utils/Permissions.cs @@ -118,6 +118,7 @@ namespace Discord //Start with this user's guild permissions resolvedPermissions = guildPermissions; + //Give/Take Role permissions OverwritePermissions? perms; var roleIds = user.RoleIds; if (roleIds.Count > 0) @@ -128,15 +129,17 @@ namespace Discord perms = channel.GetPermissionOverwrite(guild.GetRole(roleId)); if (perms != null) { - deniedPermissions |= perms.Value.DenyValue; allowedPermissions |= perms.Value.AllowValue; + deniedPermissions |= perms.Value.DenyValue; } } - resolvedPermissions = (resolvedPermissions & ~deniedPermissions) | allowedPermissions; + resolvedPermissions = (resolvedPermissions | allowedPermissions) & ~deniedPermissions; } + + //Give/Take User permissions perms = channel.GetPermissionOverwrite(user); if (perms != null) - resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; + resolvedPermissions = (resolvedPermissions | perms.Value.AllowValue) & ~perms.Value.DenyValue; //TODO: C#7 Typeswitch candidate var textChannel = channel as ITextChannel; From f3aa54640705590618bb758f80977ec943be173b Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 12 Feb 2017 19:06:36 -0500 Subject: [PATCH 237/263] Throw an InvalidOp if a user tries to set the DefaultRunMode to Default never overestimate the end user --- src/Discord.Net.Commands/CommandService.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index 9f6e66c20..2c7955028 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -35,6 +35,8 @@ namespace Discord.Commands _caseSensitive = config.CaseSensitiveCommands; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; + if (_defaultRunMode == RunMode.Default) + throw new InvalidOperationException("The default run mode cannot be set to Default, it must be one of Sync, Mixed, or Async"); _moduleLock = new SemaphoreSlim(1, 1); _typedModuleDefs = new ConcurrentDictionary(); From 9c3f858b42dad07cd2984aeab660d401404f7d8a Mon Sep 17 00:00:00 2001 From: Emzi0767 Date: Mon, 13 Feb 2017 22:22:06 +0100 Subject: [PATCH 238/263] Fixed couple non-default arguments in REST messages --- src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index b064e67b0..ee806dbc1 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -128,7 +128,7 @@ namespace Discord.Rest } } - public async Task ModifyAsync(Action func, RequestOptions options) + public async Task ModifyAsync(Action func, RequestOptions options = null) { var model = await MessageHelper.ModifyAsync(this, Discord, func, options).ConfigureAwait(false); Update(model); @@ -151,9 +151,9 @@ namespace Discord.Rest => MessageHelper.GetReactionUsersAsync(this, emoji, x => { x.Limit = limit; x.AfterUserId = afterUserId.HasValue ? afterUserId.Value : Optional.Create(); }, Discord, options); - public Task PinAsync(RequestOptions options) + public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); - public Task UnpinAsync(RequestOptions options) + public Task UnpinAsync(RequestOptions options = null) => MessageHelper.UnpinAsync(this, Discord, options); public string Resolve(int startIndex, TagHandling userHandling = TagHandling.Name, TagHandling channelHandling = TagHandling.Name, From e3d31c5bd1e2fddc8413c59a8d4e21b1fa5bd30b Mon Sep 17 00:00:00 2001 From: RyadaProductions Date: Wed, 15 Feb 2017 13:26:25 +0100 Subject: [PATCH 239/263] Update status.cs SetStatus and SetGame don't exist, only SetStatusAsync and SetGameAsync --- docs/guides/samples/faq/status.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/samples/faq/status.cs b/docs/guides/samples/faq/status.cs index 8025dd7fd..18906c53b 100644 --- a/docs/guides/samples/faq/status.cs +++ b/docs/guides/samples/faq/status.cs @@ -1,5 +1,5 @@ public async Task ModifyStatus() { - await _client.SetStatus(UserStatus.Idle); - await _client.SetGame("Type !help for help"); + await _client.SetStatusAsync(UserStatus.Idle); + await _client.SetGameAsync("Type !help for help"); } From e601cea2555a0aa4e41ddcc73e8f7ac4dab39b8e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 15 Feb 2017 15:59:23 -0500 Subject: [PATCH 240/263] Add Roles property to SocketGuildUser there's a few different ways to select a user's roles from their role IDs, and this one seems to be the most efficient. doesn't seem like there's any reason this shouldn't be included. --- src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 3d827bca5..55c676901 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using Model = Discord.API.GuildMember; using PresenceModel = Discord.API.Presence; @@ -32,6 +33,7 @@ namespace Discord.WebSocket public bool IsDeafened => VoiceState?.IsDeafened ?? false; public bool IsMuted => VoiceState?.IsMuted ?? false; public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); + public IEnumerable Roles => _roleIds.Select(id => Guild.GetRole(id)); public IReadOnlyCollection RoleIds => _roleIds; public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; public string VoiceSessionId => VoiceState?.VoiceSessionId ?? ""; From 29261a943a02ab0f5730866feac3fe5acc67983a Mon Sep 17 00:00:00 2001 From: james7132 Date: Wed, 15 Feb 2017 21:44:31 +0000 Subject: [PATCH 241/263] Fix typo --- docs/guides/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/commands.md b/docs/guides/commands.md index 71fdc58e9..8f1a34db9 100644 --- a/docs/guides/commands.md +++ b/docs/guides/commands.md @@ -168,7 +168,7 @@ IDependencyMap as an argument and extract services yourself. ### Module Properties -Modules with public settable properties will be have them injected after +Modules with public settable properties will have them injected after module construction. ### Module Groups From f4918c793beb0447a3827cf6b3ad2e49293ce783 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Tue, 21 Feb 2017 06:06:06 +0100 Subject: [PATCH 242/263] Add comment about message cache --- docs/guides/samples/first-steps.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/guides/samples/first-steps.cs b/docs/guides/samples/first-steps.cs index 01544e03f..3f1377ed7 100644 --- a/docs/guides/samples/first-steps.cs +++ b/docs/guides/samples/first-steps.cs @@ -27,6 +27,11 @@ class Program { // How much logging do you want to see? LogLevel = LogSeverity.Info, + + // If you or another service needs to do anything with messages + // (eg. checking Reactions), you should probably + // set the MessageCacheSize here. + //MessageCacheSize = 50, // If your platform doesn't have native websockets, // add Discord.Net.Providers.WS4Net from NuGet, From ab60f635116e509df54f7d76253c6edc2e556f7b Mon Sep 17 00:00:00 2001 From: "Sindre G. Langhus" Date: Tue, 21 Feb 2017 22:11:48 +0900 Subject: [PATCH 243/263] Changes HasValue in Cachable to constructor argument --- src/Discord.Net.Core/Utils/Cacheable.cs | 5 +++-- .../DiscordSocketClient.cs | 20 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Discord.Net.Core/Utils/Cacheable.cs b/src/Discord.Net.Core/Utils/Cacheable.cs index da6c660b8..10b61be90 100644 --- a/src/Discord.Net.Core/Utils/Cacheable.cs +++ b/src/Discord.Net.Core/Utils/Cacheable.cs @@ -16,7 +16,7 @@ namespace Discord /// /// Is this entity cached? /// - public bool HasValue => !EqualityComparer.Default.Equals(Value, default(TEntity)); + public bool HasValue { get; } /// /// The ID of this entity. /// @@ -30,10 +30,11 @@ namespace Discord public TEntity Value { get; } private Func> DownloadFunc { get; } - internal Cacheable(TEntity value, TId id, Func> downloadFunc) + internal Cacheable(TEntity value, TId id, bool hasValue , Func> downloadFunc) { Value = value; Id = id; + HasValue = hasValue; DownloadFunc = downloadFunc; } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 60ebeb535..891db6307 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1273,7 +1273,8 @@ namespace Discord.WebSocket SocketMessage before = null, after = null; SocketMessage cachedMsg = channel.GetCachedMessage(data.Id); - if (cachedMsg != null) + bool isCached = cachedMsg != null; + if (isCached) { before = cachedMsg.Clone(); cachedMsg.Update(State, data); @@ -1292,7 +1293,7 @@ namespace Discord.WebSocket after = SocketMessage.Create(this, State, author, channel, data); } - var cacheableBefore = new Cacheable(before, data.Id, async () => await channel.GetMessageAsync(data.Id)); + var cacheableBefore = new Cacheable(before, data.Id, isCached , async () => await channel.GetMessageAsync(data.Id)); await _messageUpdatedEvent.InvokeAsync(cacheableBefore, after, channel).ConfigureAwait(false); } @@ -1318,7 +1319,8 @@ namespace Discord.WebSocket } var msg = SocketChannelHelper.RemoveMessage(channel, this, data.Id); - var cacheable = new Cacheable(msg, data.Id, async () => await channel.GetMessageAsync(data.Id)); + bool isCached = msg != null; + var cacheable = new Cacheable(msg, data.Id, isCached, async () => await channel.GetMessageAsync(data.Id)); await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } @@ -1338,9 +1340,10 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + bool isCached = cachedMsg != null; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); + var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.AddReaction(reaction); @@ -1362,9 +1365,10 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; + bool isCached = cachedMsg != null; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); + var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.RemoveReaction(reaction); @@ -1386,7 +1390,8 @@ namespace Discord.WebSocket if (channel != null) { SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; - var cacheable = new Cacheable(cachedMsg, data.MessageId, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); + bool isCached = cachedMsg != null; + var cacheable = new Cacheable(cachedMsg, data.MessageId, isCached, async () => await channel.GetMessageAsync(data.MessageId) as IUserMessage); cachedMsg?.ClearReactions(); @@ -1416,7 +1421,8 @@ namespace Discord.WebSocket foreach (var id in data.Ids) { var msg = SocketChannelHelper.RemoveMessage(channel, this, id); - var cacheable = new Cacheable(msg, id, async () => await channel.GetMessageAsync(id)); + bool isCached = msg != null; + var cacheable = new Cacheable(msg, id, isCached, async () => await channel.GetMessageAsync(id)); await _messageDeletedEvent.InvokeAsync(cacheable, channel).ConfigureAwait(false); } } From bb9f144eae13794463af2cabff4f4554aee2b826 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Tue, 21 Feb 2017 16:19:33 -0500 Subject: [PATCH 244/263] Remove implicit SocketGuildUser.RoleIds; refactor Roles to ReadOnly --- src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs index 55c676901..5162839d7 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketGuildUser.cs @@ -33,8 +33,7 @@ namespace Discord.WebSocket public bool IsDeafened => VoiceState?.IsDeafened ?? false; public bool IsMuted => VoiceState?.IsMuted ?? false; public DateTimeOffset? JoinedAt => DateTimeUtils.FromTicks(_joinedAtTicks); - public IEnumerable Roles => _roleIds.Select(id => Guild.GetRole(id)); - public IReadOnlyCollection RoleIds => _roleIds; + public IEnumerable Roles => _roleIds.Select(id => Guild.GetRole(id)).ToReadOnlyCollection(() => _roleIds.Count()); public SocketVoiceChannel VoiceChannel => VoiceState?.VoiceChannel; public string VoiceSessionId => VoiceState?.VoiceSessionId ?? ""; public SocketVoiceState? VoiceState => Guild.GetVoiceState(Id); @@ -117,7 +116,7 @@ namespace Discord.WebSocket //IGuildUser IGuild IGuildUser.Guild => Guild; ulong IGuildUser.GuildId => Guild.Id; - IReadOnlyCollection IGuildUser.RoleIds => RoleIds; + IReadOnlyCollection IGuildUser.RoleIds => _roleIds; //IUser Task IUser.GetDMChannelAsync(CacheMode mode, RequestOptions options) From 061935cc9fd4c7465b4d4f3b727dbf8e9ffff597 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Tue, 21 Feb 2017 16:29:40 -0500 Subject: [PATCH 245/263] Clean up #501 --- docs/index.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/index.md b/docs/index.md index 447fccb10..3f0393d4f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,10 +3,11 @@ Discord.Net is an asynchronous, multiplatform .NET Library used to interface with the [Discord API](https://discordapp.com/). -If this is your first time using Discord.Net, you should refer to the [Intro](guides/intro.md) for tutorials. More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. +If this is your first time using Discord.Net, you should refer to the [Intro](guides/intro.md) for tutorials. +More experienced users might refer to the [API Documentation](api/index.md) for a breakdown of the individuals objects in the library. For additional resources: - - [Discord API Guild](https://discord.gg/discord-api) (Go to the #dotnet_discord-net channel) - - [GitHub Repo](https://github.com/RogueException/Discord.Net/tree/dev) (**todo:** Point this to Master after Release.) + - [Discord API Guild](https://discord.gg/discord-api) - Look for `#dotnet_discord-net` + - [GitHub](https://github.com/RogueException/Discord.Net/tree/dev) - [NuGet](https://www.nuget.org/packages/Discord.Net/) - - [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) (For Unstable Releases and Community-Contributed Addons) + - [MyGet Feed](https://www.myget.org/feed/Packages/discord-net) - Addons and nightly builds \ No newline at end of file From 87f404c23b9938a81a6604c96344aaef34711f38 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Tue, 21 Feb 2017 16:36:06 -0500 Subject: [PATCH 246/263] Move logging lambda to its own method --- docs/guides/samples/logging.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/guides/samples/logging.cs b/docs/guides/samples/logging.cs index 05d8e9496..fd72daf2b 100644 --- a/docs/guides/samples/logging.cs +++ b/docs/guides/samples/logging.cs @@ -12,15 +12,17 @@ public class Program LogLevel = LogSeverity.Info }); - _client.Log += (message) => - { - Console.WriteLine($"{message.ToString()}"); - return Task.CompletedTask; - }; + _client.Log += Log; await _client.LoginAsync(TokenType.Bot, "bot token"); await _client.ConnectAsync(); await Task.Delay(-1); } + + private Task Log(LogMessage message) + { + Console.WriteLine(message.ToString()); + return Task.CompletedTask; + } } From 7476c4ca38e489e0772de61ab132ff2e657b542b Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 23 Feb 2017 15:47:46 -0500 Subject: [PATCH 247/263] Cleanup property injection --- .../Utilities/ReflectionUtils.cs | 38 ++++++++----------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs index 230b30312..1333b9640 100644 --- a/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs +++ b/src/Discord.Net.Commands/Utilities/ReflectionUtils.cs @@ -30,17 +30,7 @@ namespace Discord.Commands for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; - object arg; - if (map == null || !map.TryGet(parameter.ParameterType, out arg)) - { - if (parameter.ParameterType == typeof(CommandService)) - arg = service; - else if (parameter.ParameterType == typeof(IDependencyMap)) - arg = map; - else - throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\", dependency \"{parameter.ParameterType.Name}\" was not found."); - } - args[i] = arg; + args[i] = GetMember(parameter.ParameterType, map, service, typeInfo); } T obj; @@ -55,21 +45,25 @@ namespace Discord.Commands foreach(var property in properties) { - object prop; - if (map == null || !map.TryGet(property.PropertyType, out prop)) - { - if (property.PropertyType == typeof(CommandService)) - prop = service; - else if (property.PropertyType == typeof(IDependencyMap)) - prop = map; - else - throw new InvalidOperationException($"Failed to create \"{typeInfo.FullName}\", dependency \"{property.PropertyType.Name}\" was not found."); - } - property.SetValue(obj, prop); + property.SetValue(obj, GetMember(property.PropertyType, map, service, typeInfo)); } return obj; }; } + internal static object GetMember(Type targetType, IDependencyMap map, CommandService service, TypeInfo baseType) + { + object arg; + if (map == null || !map.TryGet(targetType, out arg)) + { + if (targetType == typeof(CommandService)) + arg = service; + else if (targetType == typeof(IDependencyMap)) + arg = map; + else + throw new InvalidOperationException($"Failed to create \"{baseType.FullName}\", dependency \"{targetType.Name}\" was not found."); + } + return arg; + } } } From 6116c1bcefc8b2bbf4e93a518075ee9a550c9ac3 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 22 Dec 2016 22:46:30 -0500 Subject: [PATCH 248/263] Replace IUser.AvatarUrl with IUser#GetAvatarUrl(size) This is to support the new user image endpoint, which provides a parameter for image resizing. --- src/Discord.Net.Core/CDN.cs | 4 ++-- src/Discord.Net.Core/DiscordConfig.cs | 1 + src/Discord.Net.Core/Entities/Users/IUser.cs | 2 +- src/Discord.Net.Rest/Entities/Users/RestUser.cs | 2 +- src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 2 +- src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 98538e3c8..5348217e7 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -4,8 +4,8 @@ { public static string GetApplicationIconUrl(ulong appId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; - public static string GetUserAvatarUrl(ulong userId, string avatarId) - => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.jpg" : null; + public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size) + => avatarId != null ? $"{DiscordConfig.AvatarUrl}avatars/{userId}/{avatarId}.webp?size={size}" : null; public static string GetGuildIconUrl(ulong guildId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null; public static string GetGuildSplashUrl(ulong guildId, string splashId) diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 78a5b0e1e..8a4f9496e 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -12,6 +12,7 @@ namespace Discord public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/"; public const string CDNUrl = "https://cdn.discordapp.com/"; + public const string AvatarUrl = "https://images.discordapp.net/"; public const string InviteUrl = "https://discord.gg/"; public const int DefaultRequestTimeout = 15000; diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs index c02f8aeca..deb389800 100644 --- a/src/Discord.Net.Core/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -7,7 +7,7 @@ namespace Discord /// Gets the id of this user's avatar. string AvatarId { get; } /// Gets the url to this user's avatar. - string AvatarUrl { get; } + string GetAvatarUrl(ushort size = 1024); /// Gets the per-username unique id for this user. string Discriminator { get; } /// Gets the per-username unique id for this user. diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 9b43b88ac..380d9c0a3 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -13,7 +13,7 @@ namespace Discord.Rest public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); + public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index 9f30dc53f..fde165ad0 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -14,7 +14,7 @@ namespace Discord.Rpc public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); + public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 393bc22b5..ecb835763 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -15,7 +15,7 @@ namespace Discord.WebSocket internal abstract SocketGlobalUser GlobalUser { get; } internal abstract SocketPresence Presence { get; set; } - public string AvatarUrl => CDN.GetUserAvatarUrl(Id, AvatarId); + public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); From a5adc8a129adccc3accf2cdb0833c4df0e099b85 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 23 Feb 2017 16:04:13 -0500 Subject: [PATCH 249/263] Cleanup from rebase --- src/Discord.Net.Core/CDN.cs | 2 +- src/Discord.Net.Core/DiscordConfig.cs | 1 - src/Discord.Net.Rest/Entities/Users/RestUser.cs | 2 +- src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 2 +- src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 5348217e7..4220217b5 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -5,7 +5,7 @@ public static string GetApplicationIconUrl(ulong appId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size) - => avatarId != null ? $"{DiscordConfig.AvatarUrl}avatars/{userId}/{avatarId}.webp?size={size}" : null; + => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.webp?size={size}" : null; public static string GetGuildIconUrl(ulong guildId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null; public static string GetGuildSplashUrl(ulong guildId, string splashId) diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 8a4f9496e..78a5b0e1e 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -12,7 +12,6 @@ namespace Discord public static readonly string ClientAPIUrl = $"https://discordapp.com/api/v{APIVersion}/"; public const string CDNUrl = "https://cdn.discordapp.com/"; - public const string AvatarUrl = "https://images.discordapp.net/"; public const string InviteUrl = "https://discord.gg/"; public const int DefaultRequestTimeout = 15000; diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 380d9c0a3..31036101b 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -13,7 +13,7 @@ namespace Discord.Rest public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index fde165ad0..d8abfe759 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -14,7 +14,7 @@ namespace Discord.Rpc public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index ecb835763..30c181b02 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -15,7 +15,7 @@ namespace Discord.WebSocket internal abstract SocketGlobalUser GlobalUser { get; } internal abstract SocketPresence Presence { get; set; } - public string GetAvatarUrl(ushort size) => API.CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); From c486f0ee503c3055e7c3ee06f72e145798aa66b6 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 23 Feb 2017 16:11:34 -0500 Subject: [PATCH 250/263] Don't force avatar formats --- src/Discord.Net.Core/CDN.cs | 4 ++-- src/Discord.Net.Core/Entities/Users/AvatarFormat.cs | 10 ++++++++++ src/Discord.Net.Core/Entities/Users/IUser.cs | 2 +- src/Discord.Net.Rest/Entities/Users/RestUser.cs | 2 +- src/Discord.Net.Rpc/Entities/Users/RpcUser.cs | 2 +- src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs | 2 +- 6 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Users/AvatarFormat.cs diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 4220217b5..b7a5346ea 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -4,8 +4,8 @@ { public static string GetApplicationIconUrl(ulong appId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}app-icons/{appId}/{iconId}.jpg" : null; - public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size) - => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.webp?size={size}" : null; + public static string GetUserAvatarUrl(ulong userId, string avatarId, ushort size, AvatarFormat format) + => avatarId != null ? $"{DiscordConfig.CDNUrl}avatars/{userId}/{avatarId}.{format.ToString().ToLower()}?size={size}" : null; public static string GetGuildIconUrl(ulong guildId, string iconId) => iconId != null ? $"{DiscordConfig.CDNUrl}icons/{guildId}/{iconId}.jpg" : null; public static string GetGuildSplashUrl(ulong guildId, string splashId) diff --git a/src/Discord.Net.Core/Entities/Users/AvatarFormat.cs b/src/Discord.Net.Core/Entities/Users/AvatarFormat.cs new file mode 100644 index 000000000..29c17cede --- /dev/null +++ b/src/Discord.Net.Core/Entities/Users/AvatarFormat.cs @@ -0,0 +1,10 @@ +namespace Discord +{ + public enum AvatarFormat + { + WebP, + Png, + Jpeg, + Gif, + } +} diff --git a/src/Discord.Net.Core/Entities/Users/IUser.cs b/src/Discord.Net.Core/Entities/Users/IUser.cs index deb389800..b7e807d8e 100644 --- a/src/Discord.Net.Core/Entities/Users/IUser.cs +++ b/src/Discord.Net.Core/Entities/Users/IUser.cs @@ -7,7 +7,7 @@ namespace Discord /// Gets the id of this user's avatar. string AvatarId { get; } /// Gets the url to this user's avatar. - string GetAvatarUrl(ushort size = 1024); + string GetAvatarUrl(AvatarFormat format = AvatarFormat.Png, ushort size = 128); /// Gets the per-username unique id for this user. string Discriminator { get; } /// Gets the per-username unique id for this user. diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 31036101b..b439fb886 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -13,7 +13,7 @@ namespace Discord.Rest public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(AvatarFormat format = AvatarFormat.Png, ushort size = 128) => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs index d8abfe759..e78aee008 100644 --- a/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs +++ b/src/Discord.Net.Rpc/Entities/Users/RpcUser.cs @@ -14,7 +14,7 @@ namespace Discord.Rpc public ushort DiscriminatorValue { get; private set; } public string AvatarId { get; private set; } - public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(AvatarFormat format = AvatarFormat.Png, ushort size = 128) => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index 30c181b02..574e79b6e 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -15,7 +15,7 @@ namespace Discord.WebSocket internal abstract SocketGlobalUser GlobalUser { get; } internal abstract SocketPresence Presence { get; set; } - public string GetAvatarUrl(ushort size) => CDN.GetUserAvatarUrl(Id, AvatarId, size); + public string GetAvatarUrl(AvatarFormat format = AvatarFormat.Png, ushort size = 128) => CDN.GetUserAvatarUrl(Id, AvatarId, size, format); public DateTimeOffset CreatedAt => DateTimeUtils.FromSnowflake(Id); public string Discriminator => DiscriminatorValue.ToString("D4"); public string Mention => MentionUtils.MentionUser(Id); From 607f478b9a979b98b020ff8964982bed3c75e22e Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 23 Feb 2017 17:39:10 -0500 Subject: [PATCH 251/263] Check that messages older than two weeks are not passed to bulk delete This resolves #477 --- src/Discord.Net.Core/Utils/DateTimeUtils.cs | 2 ++ src/Discord.Net.Core/Utils/Preconditions.cs | 8 ++++++++ src/Discord.Net.Rest/DiscordRestApiClient.cs | 1 + 3 files changed, 11 insertions(+) diff --git a/src/Discord.Net.Core/Utils/DateTimeUtils.cs b/src/Discord.Net.Core/Utils/DateTimeUtils.cs index b36d329d1..fc9ef4b7b 100644 --- a/src/Discord.Net.Core/Utils/DateTimeUtils.cs +++ b/src/Discord.Net.Core/Utils/DateTimeUtils.cs @@ -12,6 +12,8 @@ namespace Discord public static DateTimeOffset FromSnowflake(ulong value) => FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); + public static ulong ToSnowflake(DateTimeOffset value) + => (ulong)(ToUnixMilliseconds(value) - 1420070400000L) << 22; public static DateTimeOffset FromTicks(long ticks) => new DateTimeOffset(ticks, TimeSpan.Zero); diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 14a730b7e..c246538d8 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -181,5 +181,13 @@ namespace Discord if (msg == null) return new ArgumentException($"Value must be less than {value}", name); else return new ArgumentException(msg, name); } + + // Bulk Delete + public static void NoMessageOlderThanTwoWeeks(ulong[] collection, string name) + { + var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.Now.Subtract(TimeSpan.FromDays(14))); + for (var i = 0; i < collection.Length; i++) + if (collection[i] <= minimum) throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks to delete."); + } } } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 065a25b55..0700b69ea 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -484,6 +484,7 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); + Preconditions.NoMessageOlderThanTwoWeeks(args.MessageIds, nameof(args.MessageIds)); options = RequestOptions.CreateOrClone(options); switch (args.MessageIds.Length) From d321ad3e5c46d7a9b52c3af8239353f013e8185b Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 17 Feb 2017 23:06:18 -0400 Subject: [PATCH 252/263] Moved Frame models, added default providers --- src/Discord.Net.Analyzers/AssemblyInfo.cs | 3 +++ src/Discord.Net.Rest/DiscordRestConfig.cs | 2 +- .../Net/DefaultRestClientProvider.cs | 19 +++++++++++++ .../API/RpcFrame.cs | 0 .../API/SocketFrame.cs | 0 .../DiscordShardedClient.cs | 2 +- .../DiscordSocketClient.cs | 5 ++-- .../Net/DefaultUdpSocketProvider.cs | 27 +++++++++++++++++++ .../Net/DefaultWebSocketClient.cs | 4 --- .../Net/DefaultWebSocketClientProvider.cs | 27 +++++++++++++++++++ 10 files changed, 81 insertions(+), 8 deletions(-) create mode 100644 src/Discord.Net.Analyzers/AssemblyInfo.cs create mode 100644 src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs rename src/{Discord.Net.Rest => Discord.Net.Rpc}/API/RpcFrame.cs (100%) rename src/{Discord.Net.Rest => Discord.Net.WebSocket}/API/SocketFrame.cs (100%) create mode 100644 src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs create mode 100644 src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs diff --git a/src/Discord.Net.Analyzers/AssemblyInfo.cs b/src/Discord.Net.Analyzers/AssemblyInfo.cs new file mode 100644 index 000000000..5e9efa5bc --- /dev/null +++ b/src/Discord.Net.Analyzers/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file diff --git a/src/Discord.Net.Rest/DiscordRestConfig.cs b/src/Discord.Net.Rest/DiscordRestConfig.cs index 33a3cb4e8..c3cd70683 100644 --- a/src/Discord.Net.Rest/DiscordRestConfig.cs +++ b/src/Discord.Net.Rest/DiscordRestConfig.cs @@ -10,6 +10,6 @@ namespace Discord.Rest internal const int WebSocketQueueInterval = 100; /// Gets or sets the provider used to generate new REST connections. - public RestClientProvider RestClientProvider { get; set; } = url => new DefaultRestClient(url); + public RestClientProvider RestClientProvider { get; set; } = DefaultRestClientProvider.Instance; } } diff --git a/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs b/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs new file mode 100644 index 000000000..311a53562 --- /dev/null +++ b/src/Discord.Net.Rest/Net/DefaultRestClientProvider.cs @@ -0,0 +1,19 @@ +using System; + +namespace Discord.Net.Rest +{ + public static class DefaultRestClientProvider + { + public static readonly RestClientProvider Instance = url => + { + try + { + return new DefaultRestClient(url); + } + catch (PlatformNotSupportedException ex) + { + throw new PlatformNotSupportedException("The default RestClientProvider is not supported on this platform.", ex); + } + }; + } +} diff --git a/src/Discord.Net.Rest/API/RpcFrame.cs b/src/Discord.Net.Rpc/API/RpcFrame.cs similarity index 100% rename from src/Discord.Net.Rest/API/RpcFrame.cs rename to src/Discord.Net.Rpc/API/RpcFrame.cs diff --git a/src/Discord.Net.Rest/API/SocketFrame.cs b/src/Discord.Net.WebSocket/API/SocketFrame.cs similarity index 100% rename from src/Discord.Net.Rest/API/SocketFrame.cs rename to src/Discord.Net.WebSocket/API/SocketFrame.cs diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 832e35578..e897a0b40 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -70,7 +70,7 @@ namespace Discord.WebSocket } } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, config.WebSocketProvider); + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); protected override async Task OnLoginAsync(TokenType tokenType, string token) { diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index fa6995c27..ea0250c9b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -142,7 +142,7 @@ namespace Discord.WebSocket _largeGuilds = new ConcurrentQueue(); } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent, config.WebSocketProvider); + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); protected override async Task OnLoginAsync(TokenType tokenType, string token) { @@ -232,7 +232,8 @@ namespace Discord.WebSocket ConnectionState = ConnectionState.Connected; await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); - await ProcessUserDownloadsAsync(_downloadUsersFor.Select(x => GetGuild(x)).Where(x => x != null).ToImmutableArray()).ConfigureAwait(false); + await ProcessUserDownloadsAsync(_downloadUsersFor.Select(x => GetGuild(x)) + .Where(x => x != null).ToImmutableArray()).ConfigureAwait(false); } catch (Exception) { diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs new file mode 100644 index 000000000..cba4fecb0 --- /dev/null +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs @@ -0,0 +1,27 @@ +using System; + +namespace Discord.Net.Udp +{ + public static class DefaultUdpSocketProvider + { +#if NETSTANDARD1_3 + public static readonly UdpSocketProvider Instance = () => + { + try + { + return new DefaultUdpSocket(); + } + catch (PlatformNotSupportedException ex) + { + throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.", ex); + } + }; +#else + public static readonly UdpSocketProvider Instance = () => + { + throw new PlatformNotSupportedException("The default UdpSocketProvider is not supported on this platform.\n" + + "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } +} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs index aa8bb6986..6f667ae41 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -206,15 +206,11 @@ namespace Discord.Net.WebSockets //Use the internal buffer if we can get it resultCount = (int)stream.Length; -#if NETSTANDARD1_3 ArraySegment streamBuffer; if (stream.TryGetBuffer(out streamBuffer)) result = streamBuffer.Array; else result = stream.ToArray(); -#else - result = stream.ToArray(); -#endif } } else diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs new file mode 100644 index 000000000..d93ded57d --- /dev/null +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs @@ -0,0 +1,27 @@ +using System; + +namespace Discord.Net.WebSockets +{ + public static class DefaultWebSocketProvider + { +#if NETSTANDARD1_3 + public static readonly WebSocketProvider Instance = () => + { + try + { + return new DefaultWebSocketClient(); + } + catch (PlatformNotSupportedException ex) + { + throw new PlatformNotSupportedException("The default WebSocketProvider is not supported on this platform.", ex); + } + }; +#else + public static readonly WebSocketProvider Instance = () => + { + throw new PlatformNotSupportedException("The default WebSocketProvider is not supported on this platform.\n" + + "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); + }; +#endif + } +} \ No newline at end of file From 8630185ac92260df54b5f9ab7f6dfd26768caa6b Mon Sep 17 00:00:00 2001 From: RogueException Date: Fri, 17 Feb 2017 23:12:24 -0400 Subject: [PATCH 253/263] Started Discord.Net.Relay --- Discord.Net.sln | 19 +++- src/Discord.Net.Core/AssemblyInfo.cs | 1 + .../ApplicationBuilderExtensions.cs | 20 ++++ src/Discord.Net.Relay/AssemblyInfo.cs | 3 + .../Discord.Net.Relay.csproj | 32 ++++++ src/Discord.Net.Relay/RelayConnection.cs | 79 ++++++++++++++ src/Discord.Net.Relay/RelayServer.cs | 103 ++++++++++++++++++ src/Discord.Net.WebSocket/AssemblyInfo.cs | 1 + .../DiscordSocketApiClient.cs | 11 +- .../DiscordSocketClient.cs | 2 +- .../DiscordSocketConfig.cs | 41 +------ 11 files changed, 269 insertions(+), 43 deletions(-) create mode 100644 src/Discord.Net.Relay/ApplicationBuilderExtensions.cs create mode 100644 src/Discord.Net.Relay/AssemblyInfo.cs create mode 100644 src/Discord.Net.Relay/Discord.Net.Relay.csproj create mode 100644 src/Discord.Net.Relay/RelayConnection.cs create mode 100644 src/Discord.Net.Relay/RelayServer.cs diff --git a/Discord.Net.sln b/Discord.Net.sln index 25b6e0386..6308b4444 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26014.0 @@ -25,6 +25,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\Discord.Net.Tests\Discord.Net.Tests.csproj", "{C38E5BC1-11CB-4101-8A38-5B40A1BC6433}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F66D75C0-E304-46E0-9C3A-294F340DB37D}" +EndProject +Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "Discord.Net.Relay", "src\Discord.Net.Relay\Discord.Net.Relay.csproj", "{2705FCB3-68C9-4CEB-89CC-01F8EC80512B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -143,6 +147,18 @@ Global {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x64.Build.0 = Release|x64 {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.ActiveCfg = Release|x86 {C38E5BC1-11CB-4101-8A38-5B40A1BC6433}.Release|x86.Build.0 = Release|x86 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x64.ActiveCfg = Debug|x64 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x64.Build.0 = Debug|x64 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x86.ActiveCfg = Debug|x86 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Debug|x86.Build.0 = Debug|x86 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|Any CPU.Build.0 = Release|Any CPU + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x64.ActiveCfg = Release|x64 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x64.Build.0 = Release|x64 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x86.ActiveCfg = Release|x86 + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -154,5 +170,6 @@ Global {688FD1D8-7F01-4539-B2E9-F473C5D699C7} = {288C363D-A636-4EAE-9AC1-4698B641B26E} {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} {ABC9F4B9-2452-4725-B522-754E0A02E282} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} + {2705FCB3-68C9-4CEB-89CC-01F8EC80512B} = {F66D75C0-E304-46E0-9C3A-294F340DB37D} EndGlobalSection EndGlobal diff --git a/src/Discord.Net.Core/AssemblyInfo.cs b/src/Discord.Net.Core/AssemblyInfo.cs index 8563c4035..c75729acf 100644 --- a/src/Discord.Net.Core/AssemblyInfo.cs +++ b/src/Discord.Net.Core/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Discord.Net.Relay")] [assembly: InternalsVisibleTo("Discord.Net.Rest")] [assembly: InternalsVisibleTo("Discord.Net.Rpc")] [assembly: InternalsVisibleTo("Discord.Net.WebSocket")] diff --git a/src/Discord.Net.Relay/ApplicationBuilderExtensions.cs b/src/Discord.Net.Relay/ApplicationBuilderExtensions.cs new file mode 100644 index 000000000..2a1e759c0 --- /dev/null +++ b/src/Discord.Net.Relay/ApplicationBuilderExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Builder; +using System; + +namespace Discord.Relay +{ + public static class ApplicationBuilderExtensions + { + public static void UseDiscordRelay(this IApplicationBuilder app, Action configAction = null) + { + var server = new RelayServer(configAction); + server.StartAsync(); + app.Use(async (context, next) => + { + if (context.WebSockets.IsWebSocketRequest) + await server.AcceptAsync(context); + await next(); + }); + } + } +} diff --git a/src/Discord.Net.Relay/AssemblyInfo.cs b/src/Discord.Net.Relay/AssemblyInfo.cs new file mode 100644 index 000000000..5e9efa5bc --- /dev/null +++ b/src/Discord.Net.Relay/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file diff --git a/src/Discord.Net.Relay/Discord.Net.Relay.csproj b/src/Discord.Net.Relay/Discord.Net.Relay.csproj new file mode 100644 index 000000000..8fee12d14 --- /dev/null +++ b/src/Discord.Net.Relay/Discord.Net.Relay.csproj @@ -0,0 +1,32 @@ + + + 1.0.0 + rc-dev + rc-$(BuildNumber) + netstandard1.3 + Discord.Net.Relay + RogueException + A core Discord.Net library containing the Relay server. + discord;discordapp + https://github.com/RogueException/Discord.Net + http://opensource.org/licenses/MIT + git + git://github.com/RogueException/Discord.Net + Discord.Relay + true + + + + + + + + + + + + $(NoWarn);CS1573;CS1591 + true + true + + \ No newline at end of file diff --git a/src/Discord.Net.Relay/RelayConnection.cs b/src/Discord.Net.Relay/RelayConnection.cs new file mode 100644 index 000000000..ffce74f9c --- /dev/null +++ b/src/Discord.Net.Relay/RelayConnection.cs @@ -0,0 +1,79 @@ +using Discord.API; +using Discord.API.Gateway; +using Discord.Logging; +using System; +using System.Net.WebSockets; +using System.Threading; +using System.Threading.Tasks; +using WebSocketClient = System.Net.WebSockets.WebSocket; + +namespace Discord.Relay +{ + public class RelayConnection + { + private readonly RelayServer _server; + private readonly WebSocketClient _socket; + private readonly CancellationTokenSource _cancelToken; + private readonly byte[] _inBuffer, _outBuffer; + private readonly Logger _logger; + + internal RelayConnection(RelayServer server, WebSocketClient socket, int id) + { + _server = server; + _socket = socket; + _cancelToken = new CancellationTokenSource(); + _inBuffer = new byte[4000]; + _outBuffer = new byte[4000]; + _logger = server.LogManager.CreateLogger($"Client #{id}"); + } + + internal async Task RunAsync() + { + await _logger.InfoAsync($"Connected"); + var token = _cancelToken.Token; + try + { + var segment = new ArraySegment(_inBuffer); + + //Send HELLO + await SendAsync(GatewayOpCode.Hello, new HelloEvent { HeartbeatInterval = 15000 }).ConfigureAwait(false); + + while (_socket.State == WebSocketState.Open) + { + var result = await _socket.ReceiveAsync(segment, token).ConfigureAwait(false); + if (result.MessageType == WebSocketMessageType.Close) + await _logger.WarningAsync($"Received Close {result.CloseStatus} ({result.CloseStatusDescription ?? "No Reason"})").ConfigureAwait(false); + else + await _logger.InfoAsync($"Received {result.Count} bytes"); + } + } + catch (OperationCanceledException) + { + try { await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).ConfigureAwait(false); } + catch { } + } + catch (Exception ex) + { + try { await _socket.CloseAsync(WebSocketCloseStatus.InternalServerError, ex.Message, CancellationToken.None).ConfigureAwait(false); } + catch { } + } + finally + { + await _logger.InfoAsync($"Disconnected"); + } + } + + internal void Stop() + { + _cancelToken.Cancel(); + } + + private async Task SendAsync(GatewayOpCode opCode, object payload) + { + var frame = new SocketFrame { Operation = (int)opCode, Payload = payload }; + var bytes = _server.Serialize(frame, _outBuffer); + var segment = new ArraySegment(_outBuffer, 0, bytes); + await _socket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.Relay/RelayServer.cs b/src/Discord.Net.Relay/RelayServer.cs new file mode 100644 index 000000000..4082191fb --- /dev/null +++ b/src/Discord.Net.Relay/RelayServer.cs @@ -0,0 +1,103 @@ +using Discord.API; +using Discord.Logging; +using Discord.Net.Rest; +using Discord.Net.WebSockets; +using Discord.Rest; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using WebSocketClient = System.Net.WebSockets.WebSocket; + +namespace Discord.Relay +{ + public class RelayServer + { + public event Func Log { add { _logEvent.Add(value); } remove { _logEvent.Remove(value); } } + internal readonly AsyncEvent> _logEvent = new AsyncEvent>(); + + private readonly HashSet _connections; + private readonly SemaphoreSlim _lock; + private readonly JsonSerializer _serializer; + private readonly DiscordSocketApiClient _discord; + private int _nextId; + + internal LogManager LogManager { get; } + + internal RelayServer(Action configAction) + { + _connections = new HashSet(); + _lock = new SemaphoreSlim(1, 1); + _serializer = new JsonSerializer(); + _discord = new DiscordSocketApiClient( + DefaultRestClientProvider.Instance, + DefaultWebSocketProvider.Instance, + DiscordRestConfig.UserAgent); + configAction?.Invoke(this); + + LogManager = new LogManager(LogSeverity.Debug); + LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); + } + + internal async Task AcceptAsync(HttpContext context) + { + WebSocketClient socket; + try + { + socket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); + } + catch { return; } + + var _ = Task.Run(async () => + { + var conn = new RelayConnection(this, socket, Interlocked.Increment(ref _nextId)); + await AddConnection(conn).ConfigureAwait(false); + try + { + await conn.RunAsync().ConfigureAwait(false); + } + finally { await RemoveConnection(conn).ConfigureAwait(false); } + }); + } + + internal void StartAsync() + { + Task.Run(async () => + { + await _discord.ConnectAsync().ConfigureAwait(false); + }); + } + + internal async Task AddConnection(RelayConnection conn) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + _connections.Add(conn); + } + finally { _lock.Release(); } + } + internal async Task RemoveConnection(RelayConnection conn) + { + await _lock.WaitAsync().ConfigureAwait(false); + try + { + _connections.Remove(conn); + } + finally { _lock.Release(); } + } + + internal int Serialize(object obj, byte[] buffer) + { + using (var stream = new MemoryStream(buffer)) + using (var writer = new StreamWriter(stream)) + { + _serializer.Serialize(writer, obj); + return (int)stream.Position; + } + } + } +} diff --git a/src/Discord.Net.WebSocket/AssemblyInfo.cs b/src/Discord.Net.WebSocket/AssemblyInfo.cs index c6b5997b4..ca3e05e2f 100644 --- a/src/Discord.Net.WebSocket/AssemblyInfo.cs +++ b/src/Discord.Net.WebSocket/AssemblyInfo.cs @@ -1,3 +1,4 @@ using System.Runtime.CompilerServices; +[assembly: InternalsVisibleTo("Discord.Net.Relay")] [assembly: InternalsVisibleTo("Discord.Net.Tests")] \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index fcfa76653..cbefd795c 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -33,10 +33,11 @@ namespace Discord.API public ConnectionState ConnectionState { get; private set; } - public DiscordSocketApiClient(RestClientProvider restClientProvider, string userAgent, WebSocketProvider webSocketProvider, - RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) + public DiscordSocketApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider, string userAgent, + string url = null, RetryMode defaultRetryMode = RetryMode.AlwaysRetry, JsonSerializer serializer = null) : base(restClientProvider, userAgent, defaultRetryMode, serializer) - { + { + _gatewayUrl = url; WebSocketClient = webSocketProvider(); //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) WebSocketClient.BinaryMessage += async (data, index, count) => @@ -115,9 +116,9 @@ namespace Discord.API ConnectionState = ConnectionState.Connected; } - catch (Exception) + catch { - _gatewayUrl = null; //Uncache in case the gateway url changed + _gatewayUrl = null; //Uncache in case the gateway url changed await DisconnectInternalAsync().ConfigureAwait(false); throw; } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index ea0250c9b..c0608a868 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -142,7 +142,7 @@ namespace Discord.WebSocket _largeGuilds = new ConcurrentQueue(); } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) - => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); + => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); protected override async Task OnLoginAsync(TokenType tokenType, string token) { diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index b47f62dca..78a637d0e 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -2,7 +2,6 @@ using Discord.Net.Udp; using Discord.Net.WebSockets; using Discord.Rest; -using System; namespace Discord.WebSocket { @@ -10,6 +9,9 @@ namespace Discord.WebSocket { public const string GatewayEncoding = "json"; + /// Gets or sets the websocket host to connect to. If null, the client will use the /gateway endpoint. + public string GatewayHost { get; set; } = null; + /// Gets or sets the time, in milliseconds, to wait for a connection to complete before aborting. public int ConnectionTimeout { get; set; } = 30000; @@ -38,41 +40,8 @@ namespace Discord.WebSocket public DiscordSocketConfig() { -#if NETSTANDARD1_3 - WebSocketProvider = () => - { - try - { - return new DefaultWebSocketClient(); - } - catch (PlatformNotSupportedException ex) - { - throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.", ex); - } - }; - UdpSocketProvider = () => - { - try - { - return new DefaultUdpSocket(); - } - catch (PlatformNotSupportedException ex) - { - throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.", ex); - } - }; -#else - WebSocketProvider = () => - { - throw new PlatformNotSupportedException("The default websocket provider is not supported on this platform.\n" + - "You must specify a WebSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); - }; - UdpSocketProvider = () => - { - throw new PlatformNotSupportedException("The default UDP provider is not supported on this platform.\n" + - "You must specify a UdpSocketProvider or target a runtime supporting .NET Standard 1.3, such as .NET Framework 4.6+."); - }; -#endif + WebSocketProvider = DefaultWebSocketProvider.Instance; + UdpSocketProvider = DefaultUdpSocketProvider.Instance; } internal DiscordSocketConfig Clone() => MemberwiseClone() as DiscordSocketConfig; From 3190d7e26dd2b332c0c7559209f0925ae399cdc7 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 23 Feb 2017 16:38:57 -0400 Subject: [PATCH 254/263] Moved (re)connection handling to ConnectionManager --- src/Discord.Net.Core/Audio/IAudioClient.cs | 2 +- src/Discord.Net.Core/IDiscordClient.cs | 4 +- src/Discord.Net.Core/Logging/LogMessage.cs | 9 +- .../Discord.Net.DebugTools.csproj | 2 +- src/Discord.Net.Rest/BaseDiscordClient.cs | 33 +-- src/Discord.Net.Rest/ConnectionManager.cs | 199 +++++++++++++ src/Discord.Net.Rest/DiscordRestClient.cs | 9 +- .../DiscordRpcClient.Events.cs | 4 +- src/Discord.Net.Rpc/DiscordRpcClient.cs | 201 +++---------- .../Audio/AudioClient.cs | 178 +++++------ .../DiscordShardedClient.cs | 43 +-- .../DiscordSocketApiClient.cs | 14 +- .../DiscordSocketClient.cs | 280 +++++------------- .../DiscordSocketConfig.cs | 2 +- .../Entities/Guilds/SocketGuild.cs | 41 +-- 15 files changed, 440 insertions(+), 581 deletions(-) create mode 100644 src/Discord.Net.Rest/ConnectionManager.cs diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 472ad32f1..4a6ae2e27 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -14,7 +14,7 @@ namespace Discord.Audio /// Gets the estimated round-trip latency, in milliseconds, to the gateway server. int Latency { get; } - Task DisconnectAsync(); + Task StopAsync(); /// /// Creates a new outgoing stream accepting Opus-encoded data. diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index b9a08d32d..1c5ec41c1 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -10,8 +10,8 @@ namespace Discord ConnectionState ConnectionState { get; } ISelfUser CurrentUser { get; } - Task ConnectAsync(); - Task DisconnectAsync(); + Task StartAsync(); + Task StopAsync(); Task GetApplicationInfoAsync(); diff --git a/src/Discord.Net.Core/Logging/LogMessage.cs b/src/Discord.Net.Core/Logging/LogMessage.cs index 9c3dfcfea..d1b3782be 100644 --- a/src/Discord.Net.Core/Logging/LogMessage.cs +++ b/src/Discord.Net.Core/Logging/LogMessage.cs @@ -19,7 +19,7 @@ namespace Discord } public override string ToString() => ToString(null); - public string ToString(StringBuilder builder = null, bool fullException = true, bool prependTimestamp = true, DateTimeKind timestampKind = DateTimeKind.Local, int? padSource = 9) + public string ToString(StringBuilder builder = null, bool fullException = true, bool prependTimestamp = true, DateTimeKind timestampKind = DateTimeKind.Local, int? padSource = 11) { string sourceName = Source; string message = Message; @@ -87,8 +87,11 @@ namespace Discord } if (exMessage != null) { - builder.Append(':'); - builder.AppendLine(); + if (!string.IsNullOrEmpty(Message)) + { + builder.Append(':'); + builder.AppendLine(); + } builder.Append(exMessage); } diff --git a/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj index da0c8b0fd..829951d19 100644 --- a/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj +++ b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj @@ -6,7 +6,7 @@ netstandard1.6 Discord.Net.DebugTools RogueException - A Discord.Net extension adding random helper classes for diagnosing issues. + A Discord.Net extension adding some helper classes for diagnosing issues. discord;discordapp https://github.com/RogueException/Discord.Net http://opensource.org/licenses/MIT diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index 8948c87dc..80c4cb598 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -18,10 +18,9 @@ namespace Discord.Rest public event Func LoggedOut { add { _loggedOutEvent.Add(value); } remove { _loggedOutEvent.Remove(value); } } private readonly AsyncEvent> _loggedOutEvent = new AsyncEvent>(); - internal readonly Logger _restLogger, _queueLogger; - internal readonly SemaphoreSlim _connectionLock; - private bool _isFirstLogin; - private bool _isDisposed; + internal readonly Logger _restLogger; + private readonly SemaphoreSlim _stateLock; + private bool _isFirstLogin, _isDisposed; internal API.DiscordRestApiClient ApiClient { get; } internal LogManager LogManager { get; } @@ -35,17 +34,16 @@ namespace Discord.Rest LogManager = new LogManager(config.LogLevel); LogManager.Message += async msg => await _logEvent.InvokeAsync(msg).ConfigureAwait(false); - _connectionLock = new SemaphoreSlim(1, 1); + _stateLock = new SemaphoreSlim(1, 1); _restLogger = LogManager.CreateLogger("Rest"); - _queueLogger = LogManager.CreateLogger("Queue"); _isFirstLogin = config.DisplayInitialLog; ApiClient.RequestQueue.RateLimitTriggered += async (id, info) => { if (info == null) - await _queueLogger.WarningAsync($"Preemptive Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + await _restLogger.WarningAsync($"Preemptive Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); else - await _queueLogger.WarningAsync($"Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); + await _restLogger.WarningAsync($"Rate limit triggered: {id ?? "null"}").ConfigureAwait(false); }; ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); } @@ -53,12 +51,12 @@ namespace Discord.Rest /// public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true) { - await _connectionLock.WaitAsync().ConfigureAwait(false); + await _stateLock.WaitAsync().ConfigureAwait(false); try { await LoginInternalAsync(tokenType, token).ConfigureAwait(false); } - finally { _connectionLock.Release(); } + finally { _stateLock.Release(); } } private async Task LoginInternalAsync(TokenType tokenType, string token) { @@ -86,17 +84,17 @@ namespace Discord.Rest await _loggedInEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); } + internal virtual Task OnLoginAsync(TokenType tokenType, string token) { return Task.Delay(0); } /// public async Task LogoutAsync() { - await _connectionLock.WaitAsync().ConfigureAwait(false); + await _stateLock.WaitAsync().ConfigureAwait(false); try { await LogoutInternalAsync().ConfigureAwait(false); } - finally { _connectionLock.Release(); } + finally { _stateLock.Release(); } } private async Task LogoutInternalAsync() { @@ -111,7 +109,7 @@ namespace Discord.Rest await _loggedOutEvent.InvokeAsync().ConfigureAwait(false); } - protected virtual Task OnLogoutAsync() { return Task.Delay(0); } + internal virtual Task OnLogoutAsync() { return Task.Delay(0); } internal virtual void Dispose(bool disposing) { @@ -161,8 +159,9 @@ namespace Discord.Rest Task IDiscordClient.GetVoiceRegionAsync(string id) => Task.FromResult(null); - Task IDiscordClient.ConnectAsync() { throw new NotSupportedException(); } - Task IDiscordClient.DisconnectAsync() { throw new NotSupportedException(); } - + Task IDiscordClient.StartAsync() + => Task.Delay(0); + Task IDiscordClient.StopAsync() + => Task.Delay(0); } } diff --git a/src/Discord.Net.Rest/ConnectionManager.cs b/src/Discord.Net.Rest/ConnectionManager.cs new file mode 100644 index 000000000..ab1f4790c --- /dev/null +++ b/src/Discord.Net.Rest/ConnectionManager.cs @@ -0,0 +1,199 @@ +using Discord.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord +{ + internal class ConnectionManager + { + public event Func Connected { add { _connectedEvent.Add(value); } remove { _connectedEvent.Remove(value); } } + private readonly AsyncEvent> _connectedEvent = new AsyncEvent>(); + public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } + private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); + + private readonly SemaphoreSlim _stateLock; + private readonly Logger _logger; + private readonly int _connectionTimeout; + private readonly Func _onConnecting; + private readonly Func _onDisconnecting; + + private TaskCompletionSource _connectionPromise, _readyPromise; + private CancellationTokenSource _combinedCancelToken, _reconnectCancelToken, _connectionCancelToken; + private Task _task; + + public ConnectionState State { get; private set; } + public CancellationToken CancelToken { get; private set; } + + public bool IsCompleted => _readyPromise.Task.IsCompleted; + + internal ConnectionManager(SemaphoreSlim stateLock, Logger logger, int connectionTimeout, + Func onConnecting, Func onDisconnecting, Action> clientDisconnectHandler) + { + _stateLock = stateLock; + _logger = logger; + _connectionTimeout = connectionTimeout; + _onConnecting = onConnecting; + _onDisconnecting = onDisconnecting; + + clientDisconnectHandler(ex => + { + if (ex != null) + Error(new Exception("WebSocket connection was closed", ex)); + else + Error(new Exception("WebSocket connection was closed")); + return Task.Delay(0); + }); + } + + public virtual async Task StartAsync() + { + await AcquireConnectionLock().ConfigureAwait(false); + var reconnectCancelToken = new CancellationTokenSource(); + _reconnectCancelToken = new CancellationTokenSource(); + _task = Task.Run(async () => + { + try + { + Random jitter = new Random(); + int nextReconnectDelay = 1000; + while (!reconnectCancelToken.IsCancellationRequested) + { + try + { + await ConnectAsync(reconnectCancelToken).ConfigureAwait(false); + nextReconnectDelay = 1000; //Reset delay + await _connectionPromise.Task.ConfigureAwait(false); + } + catch (OperationCanceledException ex) + { + Cancel(); //In case this exception didn't come from another Error call + await DisconnectAsync(ex, !reconnectCancelToken.IsCancellationRequested).ConfigureAwait(false); + } + catch (Exception ex) + { + Error(ex); //In case this exception didn't come from another Error call + if (!reconnectCancelToken.IsCancellationRequested) + { + await _logger.WarningAsync(ex).ConfigureAwait(false); + await DisconnectAsync(ex, true).ConfigureAwait(false); + } + else + { + await _logger.ErrorAsync(ex).ConfigureAwait(false); + await DisconnectAsync(ex, false).ConfigureAwait(false); + } + } + + if (!reconnectCancelToken.IsCancellationRequested) + { + //Wait before reconnecting + await Task.Delay(nextReconnectDelay, reconnectCancelToken.Token).ConfigureAwait(false); + nextReconnectDelay = (nextReconnectDelay * 2) + jitter.Next(-250, 250); + if (nextReconnectDelay > 60000) + nextReconnectDelay = 60000; + } + } + } + finally { _stateLock.Release(); } + }); + } + public virtual async Task StopAsync() + { + Cancel(); + var task = _task; + if (task != null) + await task.ConfigureAwait(false); + } + + private async Task ConnectAsync(CancellationTokenSource reconnectCancelToken) + { + _connectionCancelToken = new CancellationTokenSource(); + _combinedCancelToken = CancellationTokenSource.CreateLinkedTokenSource(_connectionCancelToken.Token, reconnectCancelToken.Token); + CancelToken = _combinedCancelToken.Token; + + _connectionPromise = new TaskCompletionSource(); + State = ConnectionState.Connecting; + await _logger.InfoAsync("Connecting").ConfigureAwait(false); + + try + { + var readyPromise = new TaskCompletionSource(); + _readyPromise = readyPromise; + + //Abort connection on timeout + var cancelToken = CancelToken; + var _ = Task.Run(async () => + { + try + { + await Task.Delay(_connectionTimeout, cancelToken).ConfigureAwait(false); + readyPromise.TrySetException(new TimeoutException()); + } + catch (OperationCanceledException) { } + }); + + await _onConnecting().ConfigureAwait(false); + + await _logger.InfoAsync("Connected").ConfigureAwait(false); + State = ConnectionState.Connected; + await _logger.DebugAsync("Raising Event").ConfigureAwait(false); + await _connectedEvent.InvokeAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + Error(ex); + throw; + } + } + private async Task DisconnectAsync(Exception ex, bool isReconnecting) + { + if (State == ConnectionState.Disconnected) return; + State = ConnectionState.Disconnecting; + await _logger.InfoAsync("Disconnecting").ConfigureAwait(false); + + await _onDisconnecting(ex).ConfigureAwait(false); + + await _logger.InfoAsync("Disconnected").ConfigureAwait(false); + State = ConnectionState.Disconnected; + await _disconnectedEvent.InvokeAsync(ex, isReconnecting).ConfigureAwait(false); + } + + public async Task CompleteAsync() + { + await _readyPromise.TrySetResultAsync(true).ConfigureAwait(false); + } + public async Task WaitAsync() + { + await _readyPromise.Task.ConfigureAwait(false); + } + + public void Cancel() + { + _readyPromise?.TrySetCanceled(); + _connectionPromise?.TrySetCanceled(); + _reconnectCancelToken?.Cancel(); + _connectionCancelToken?.Cancel(); + } + public void Error(Exception ex) + { + _readyPromise.TrySetException(ex); + _connectionPromise.TrySetException(ex); + _connectionCancelToken?.Cancel(); + } + public void CriticalError(Exception ex) + { + _reconnectCancelToken?.Cancel(); + Error(ex); + } + private async Task AcquireConnectionLock() + { + while (true) + { + await StopAsync().ConfigureAwait(false); + if (await _stateLock.WaitAsync(0).ConfigureAwait(false)) + break; + } + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 0727576bf..0ff1a4821 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -16,14 +16,19 @@ namespace Discord.Rest private static API.DiscordRestApiClient CreateApiClient(DiscordRestConfig config) => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); + internal override void Dispose(bool disposing) + { + if (disposing) + ApiClient.Dispose(); + } - protected override async Task OnLoginAsync(TokenType tokenType, string token) + internal override async Task OnLoginAsync(TokenType tokenType, string token) { var user = await ApiClient.GetMyUserAsync(new RequestOptions { RetryMode = RetryMode.AlwaysRetry }).ConfigureAwait(false); ApiClient.CurrentUserId = user.Id; base.CurrentUser = RestSelfUser.Create(this, user); } - protected override Task OnLogoutAsync() + internal override Task OnLogoutAsync() { _applicationInfo = null; return Task.Delay(0); diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs index 2a9ae21bf..d3c50a5ec 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs @@ -12,12 +12,12 @@ namespace Discord.Rpc remove { _connectedEvent.Remove(value); } } private readonly AsyncEvent> _connectedEvent = new AsyncEvent>(); - public event Func Disconnected + public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } - private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); public event Func Ready { add { _readyEvent.Add(value); } diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 845ba97c6..5235c98d4 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -8,28 +8,22 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Threading; using System.Threading.Tasks; +using System.Threading; namespace Discord.Rpc { public partial class DiscordRpcClient : BaseDiscordClient, IDiscordClient { - private readonly Logger _rpcLogger; private readonly JsonSerializer _serializer; - - private TaskCompletionSource _connectTask; - private CancellationTokenSource _cancelToken, _reconnectCancelToken; - private Task _reconnectTask; - private bool _canReconnect; + private readonly ConnectionManager _connection; + private readonly Logger _rpcLogger; + private readonly SemaphoreSlim _stateLock, _authorizeLock; public ConnectionState ConnectionState { get; private set; } public IReadOnlyCollection Scopes { get; private set; } public DateTimeOffset TokenExpiresAt { get; private set; } - //From DiscordRpcConfig - internal int ConnectionTimeout { get; private set; } - internal new API.DiscordRpcApiClient ApiClient => base.ApiClient as API.DiscordRpcApiClient; public new RestSelfUser CurrentUser { get { return base.CurrentUser as RestSelfUser; } private set { base.CurrentUser = value; } } public RestApplication ApplicationInfo { get; private set; } @@ -41,8 +35,11 @@ namespace Discord.Rpc public DiscordRpcClient(string clientId, string origin, DiscordRpcConfig config) : base(config, CreateApiClient(clientId, origin, config)) { - ConnectionTimeout = config.ConnectionTimeout; + _stateLock = new SemaphoreSlim(1, 1); + _authorizeLock = new SemaphoreSlim(1, 1); _rpcLogger = LogManager.CreateLogger("RPC"); + _connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, + OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => @@ -53,177 +50,52 @@ namespace Discord.Rpc ApiClient.SentRpcMessage += async opCode => await _rpcLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.ReceivedRpcEvent += ProcessMessageAsync; - ApiClient.Disconnected += async ex => - { - if (ex != null) - { - await _rpcLogger.WarningAsync($"Connection Closed", ex).ConfigureAwait(false); - await StartReconnectAsync(ex).ConfigureAwait(false); - } - else - await _rpcLogger.WarningAsync($"Connection Closed").ConfigureAwait(false); - }; } private static API.DiscordRpcApiClient CreateApiClient(string clientId, string origin, DiscordRpcConfig config) => new API.DiscordRpcApiClient(clientId, DiscordRestConfig.UserAgent, origin, config.RestClientProvider, config.WebSocketProvider); - - /// - public async Task ConnectAsync() + internal override void Dispose(bool disposing) { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try + if (disposing) { - await ConnectInternalAsync(false).ConfigureAwait(false); + StopAsync().GetAwaiter().GetResult(); + ApiClient.Dispose(); } - finally { _connectionLock.Release(); } } - private async Task ConnectInternalAsync(bool isReconnecting) - { - if (!isReconnecting && _reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) - _reconnectCancelToken.Cancel(); - var state = ConnectionState; - if (state == ConnectionState.Connecting || state == ConnectionState.Connected) - await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); + public Task StartAsync() => _connection.StartAsync(); + public Task StopAsync() => _connection.StopAsync(); - ConnectionState = ConnectionState.Connecting; - await _rpcLogger.InfoAsync("Connecting").ConfigureAwait(false); - try - { - var connectTask = new TaskCompletionSource(); - _connectTask = connectTask; - _cancelToken = new CancellationTokenSource(); - - //Abort connection on timeout - var _ = Task.Run(async () => - { - await Task.Delay(ConnectionTimeout).ConfigureAwait(false); - connectTask.TrySetException(new TimeoutException()); - }); - - await ApiClient.ConnectAsync().ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); - - await _connectTask.Task.ConfigureAwait(false); - if (!isReconnecting) - _canReconnect = true; - ConnectionState = ConnectionState.Connected; - await _rpcLogger.InfoAsync("Connected").ConfigureAwait(false); - } - catch (Exception) - { - await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); - throw; - } - } - /// - public async Task DisconnectAsync() + private async Task OnConnectingAsync() { - if (_connectTask?.TrySetCanceled() ?? false) return; - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync(null, false).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } + await _rpcLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); + await ApiClient.ConnectAsync().ConfigureAwait(false); + + await _connection.WaitAsync().ConfigureAwait(false); } - private async Task DisconnectInternalAsync(Exception ex, bool isReconnecting) + private async Task OnDisconnectingAsync(Exception ex) { - if (!isReconnecting) - { - _canReconnect = false; - - if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) - _reconnectCancelToken.Cancel(); - } - - if (ConnectionState == ConnectionState.Disconnected) return; - ConnectionState = ConnectionState.Disconnecting; - await _rpcLogger.InfoAsync("Disconnecting").ConfigureAwait(false); - - await _rpcLogger.DebugAsync("Disconnecting - CancelToken").ConfigureAwait(false); - //Signal tasks to complete - try { _cancelToken.Cancel(); } catch { } - - await _rpcLogger.DebugAsync("Disconnecting - ApiClient").ConfigureAwait(false); - //Disconnect from server + await _rpcLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); await ApiClient.DisconnectAsync().ConfigureAwait(false); - - ConnectionState = ConnectionState.Disconnected; - await _rpcLogger.InfoAsync("Disconnected").ConfigureAwait(false); - - await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); } - private async Task StartReconnectAsync(Exception ex) + public async Task AuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) { - await _connectionLock.WaitAsync().ConfigureAwait(false); + await _authorizeLock.WaitAsync().ConfigureAwait(false); try { - if (!_canReconnect || _reconnectTask != null) return; - _reconnectCancelToken = new CancellationTokenSource(); - _reconnectTask = ReconnectInternalAsync(ex, _reconnectCancelToken.Token); - } - finally { _connectionLock.Release(); } - } - private async Task ReconnectInternalAsync(Exception ex, CancellationToken cancelToken) - { - if (ex == null) - { - if (_connectTask?.TrySetCanceled() ?? false) return; + await _connection.StartAsync().ConfigureAwait(false); + await _connection.WaitAsync().ConfigureAwait(false); + var result = await ApiClient.SendAuthorizeAsync(scopes, rpcToken, options).ConfigureAwait(false); + await _connection.StopAsync().ConfigureAwait(false); + return result.Code; } - else + finally { - if (_connectTask?.TrySetException(ex) ?? false) return; - } - - try - { - Random jitter = new Random(); - int nextReconnectDelay = 1000; - while (true) - { - await Task.Delay(nextReconnectDelay, cancelToken).ConfigureAwait(false); - nextReconnectDelay = nextReconnectDelay * 2 + jitter.Next(-250, 250); - if (nextReconnectDelay > 60000) - nextReconnectDelay = 60000; - - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - if (cancelToken.IsCancellationRequested) return; - await ConnectInternalAsync(true).ConfigureAwait(false); - _reconnectTask = null; - return; - } - catch (Exception ex2) - { - await _rpcLogger.WarningAsync("Reconnect failed", ex2).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - } - catch (OperationCanceledException) - { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await _rpcLogger.DebugAsync("Reconnect cancelled").ConfigureAwait(false); - _reconnectTask = null; - } - finally { _connectionLock.Release(); } + _authorizeLock.Release(); } } - public async Task AuthorizeAsync(string[] scopes, string rpcToken = null, RequestOptions options = null) - { - await ConnectAsync().ConfigureAwait(false); - var result = await ApiClient.SendAuthorizeAsync(scopes, rpcToken, options).ConfigureAwait(false); - await DisconnectAsync().ConfigureAwait(false); - return result.Code; - } - public async Task SubscribeGlobal(RpcGlobalEvent evnt, RequestOptions options = null) { await ApiClient.SendGlobalSubscribeAsync(GetEventName(evnt), options).ConfigureAwait(false); @@ -439,8 +311,8 @@ namespace Discord.Rpc ApplicationInfo = RestApplication.Create(this, response.Application); Scopes = response.Scopes; TokenExpiresAt = response.Expires; - - var __ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete + + var __ = _connection.CompleteAsync(); await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); } catch (Exception ex) @@ -452,7 +324,7 @@ namespace Discord.Rpc } else { - var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete + var _ = _connection.CompleteAsync(); await _rpcLogger.InfoAsync("Ready").ConfigureAwait(false); } } @@ -592,6 +464,13 @@ namespace Discord.Rpc } //IDiscordClient + ConnectionState IDiscordClient.ConnectionState => _connection.State; + Task IDiscordClient.GetApplicationInfoAsync() => Task.FromResult(ApplicationInfo); + + async Task IDiscordClient.StartAsync() + => await StartAsync().ConfigureAwait(false); + async Task IDiscordClient.StopAsync() + => await StopAsync().ConfigureAwait(false); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 5bedf1786..5404227f2 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -5,6 +5,7 @@ using Discord.WebSocket; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; +using System.Collections.Concurrent; using System.Linq; using System.Text; using System.Threading; @@ -12,6 +13,7 @@ using System.Threading.Tasks; namespace Discord.Audio { + //TODO: Add audio reconnecting internal class AudioClient : IAudioClient, IDisposable { public event Func Connected @@ -34,34 +36,37 @@ namespace Discord.Audio private readonly AsyncEvent> _latencyUpdatedEvent = new AsyncEvent>(); private readonly Logger _audioLogger; - internal readonly SemaphoreSlim _connectionLock; private readonly JsonSerializer _serializer; + private readonly ConnectionManager _connection; + private readonly SemaphoreSlim _stateLock; + private readonly ConcurrentQueue _heartbeatTimes; - private TaskCompletionSource _connectTask; - private CancellationTokenSource _cancelTokenSource; private Task _heartbeatTask; - private long _heartbeatTime; - private string _url; + private long _lastMessageTime; + private string _url, _sessionId, _token; + private ulong _userId; private uint _ssrc; private byte[] _secretKey; - private bool _isDisposed; public SocketGuild Guild { get; } public DiscordVoiceAPIClient ApiClient { get; private set; } - public ConnectionState ConnectionState { get; private set; } public int Latency { get; private set; } private DiscordSocketClient Discord => Guild.Discord; + public ConnectionState ConnectionState => _connection.State; /// Creates a new REST/WebSocket discord client. internal AudioClient(SocketGuild guild, int id) { Guild = guild; - _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); - - _connectionLock = new SemaphoreSlim(1, 1); + _stateLock = new SemaphoreSlim(1, 1); + _connection = new ConnectionManager(_stateLock, _audioLogger, 30000, + OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _heartbeatTimes = new ConcurrentQueue(); + _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); + _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { @@ -76,83 +81,28 @@ namespace Discord.Audio //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedPacket += ProcessPacketAsync; - ApiClient.Disconnected += async ex => - { - if (ex != null) - await _audioLogger.WarningAsync($"Connection Closed", ex).ConfigureAwait(false); - else - await _audioLogger.WarningAsync($"Connection Closed").ConfigureAwait(false); - }; LatencyUpdated += async (old, val) => await _audioLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); } - /// - internal async Task ConnectAsync(string url, ulong userId, string sessionId, string token) + internal async Task StartAsync(string url, ulong userId, string sessionId, string token) { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await ConnectInternalAsync(url, userId, sessionId, token).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } + _url = url; + _userId = userId; + _sessionId = sessionId; + _token = token; + await _connection.StartAsync().ConfigureAwait(false); } - private async Task ConnectInternalAsync(string url, ulong userId, string sessionId, string token) - { - var state = ConnectionState; - if (state == ConnectionState.Connecting || state == ConnectionState.Connected) - await DisconnectInternalAsync(null).ConfigureAwait(false); - - ConnectionState = ConnectionState.Connecting; - await _audioLogger.InfoAsync("Connecting").ConfigureAwait(false); - try - { - _url = url; - _connectTask = new TaskCompletionSource(); - _cancelTokenSource = new CancellationTokenSource(); - - await ApiClient.ConnectAsync("wss://" + url).ConfigureAwait(false); - await ApiClient.SendIdentityAsync(userId, sessionId, token).ConfigureAwait(false); - await _connectTask.Task.ConfigureAwait(false); + public async Task StopAsync() + => await _connection.StopAsync().ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); - ConnectionState = ConnectionState.Connected; - await _audioLogger.InfoAsync("Connected").ConfigureAwait(false); - } - catch (Exception) - { - await DisconnectInternalAsync(null).ConfigureAwait(false); - throw; - } - } - /// - public async Task DisconnectAsync() + private async Task OnConnectingAsync() { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync(null).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } + await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false); + await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); } - private async Task DisconnectAsync(Exception ex) + private async Task OnDisconnectingAsync(Exception ex) { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync(ex).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - private async Task DisconnectInternalAsync(Exception ex) - { - if (ConnectionState == ConnectionState.Disconnected) return; - ConnectionState = ConnectionState.Disconnecting; - await _audioLogger.InfoAsync("Disconnecting").ConfigureAwait(false); - - //Signal tasks to complete - try { _cancelTokenSource.Cancel(); } catch { } - //Disconnect from server await ApiClient.DisconnectAsync().ConfigureAwait(false); @@ -162,17 +112,17 @@ namespace Discord.Audio await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; - ConnectionState = ConnectionState.Disconnected; - await _audioLogger.InfoAsync("Disconnected").ConfigureAwait(false); - await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); - await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); + + long time; + while (_heartbeatTimes.TryDequeue(out time)) { } + _lastMessageTime = 0; } public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _connection.CancelToken); return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); } public AudioOutStream CreateDirectOpusStream(int samplesPerFrame) @@ -184,7 +134,7 @@ namespace Discord.Audio public AudioOutStream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _cancelTokenSource.Token); + var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _connection.CancelToken); return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); } public AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate) @@ -202,6 +152,8 @@ namespace Discord.Audio private async Task ProcessMessageAsync(VoiceOpCode opCode, object payload) { + _lastMessageTime = Environment.TickCount; + try { switch (opCode) @@ -216,8 +168,7 @@ namespace Discord.Audio if (!data.Modes.Contains(DiscordVoiceAPIClient.Mode)) throw new InvalidOperationException($"Discord does not support {DiscordVoiceAPIClient.Mode}"); - _heartbeatTime = 0; - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelTokenSource.Token); + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); ApiClient.SetUdpEndpoint(_url, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); @@ -234,19 +185,17 @@ namespace Discord.Audio _secretKey = data.SecretKey; await ApiClient.SendSetSpeaking(true).ConfigureAwait(false); - var _ = _connectTask.TrySetResultAsync(true); + var _ = _connection.CompleteAsync(); } break; case VoiceOpCode.HeartbeatAck: { await _audioLogger.DebugAsync("Received HeartbeatAck").ConfigureAwait(false); - var heartbeatTime = _heartbeatTime; - if (heartbeatTime != 0) + long time; + if (_heartbeatTimes.TryDequeue(out time)) { - int latency = (int)(Environment.TickCount - _heartbeatTime); - _heartbeatTime = 0; - + int latency = (int)(Environment.TickCount - time); int before = Latency; Latency = latency; @@ -267,7 +216,7 @@ namespace Discord.Audio } private async Task ProcessPacketAsync(byte[] packet) { - if (!_connectTask.Task.IsCompleted) + if (!_connection.IsCompleted) { if (packet.Length == 70) { @@ -291,33 +240,50 @@ namespace Discord.Audio //Clean this up when Discord's session patch is live try { + await _audioLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false); while (!cancelToken.IsCancellationRequested) { + var now = Environment.TickCount; + + //Did server respond to our last heartbeat, or are we still receiving messages (long load?) + if (_heartbeatTimes.Count != 0 && (now - _lastMessageTime) > intervalMillis && + ConnectionState == ConnectionState.Connected) + { + _connection.Error(new Exception("Server missed last heartbeat")); + return; + } + _heartbeatTimes.Enqueue(now); + await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); - if (_heartbeatTime != 0) //Server never responded to our last heartbeat + try { - if (ConnectionState == ConnectionState.Connected) - { - await _audioLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false); - await DisconnectInternalAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false); - return; - } + await ApiClient.SendHeartbeatAsync().ConfigureAwait(false); } - else - _heartbeatTime = Environment.TickCount; - await ApiClient.SendHeartbeatAsync().ConfigureAwait(false); + catch (Exception ex) + { + await _audioLogger.WarningAsync("Heartbeat Errored", ex).ConfigureAwait(false); + } + + await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); } + await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + } + catch (OperationCanceledException) + { + await _audioLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + } + catch (Exception ex) + { + await _audioLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); } - catch (OperationCanceledException) { } } internal void Dispose(bool disposing) { - if (disposing && !_isDisposed) + if (disposing) { - _isDisposed = true; - DisconnectInternalAsync(null).GetAwaiter().GetResult(); + StopAsync().GetAwaiter().GetResult(); ApiClient.Dispose(); } } diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index e897a0b40..cadbda6d1 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -72,7 +72,7 @@ namespace Discord.WebSocket private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent); - protected override async Task OnLoginAsync(TokenType tokenType, string token) + internal override async Task OnLoginAsync(TokenType tokenType, string token) { if (_automaticShards) { @@ -95,7 +95,7 @@ namespace Discord.WebSocket for (int i = 0; i < _shards.Length; i++) await _shards[i].LoginAsync(tokenType, token, false); } - protected override async Task OnLogoutAsync() + internal override async Task OnLogoutAsync() { //Assume threadsafe: already in a connection lock for (int i = 0; i < _shards.Length; i++) @@ -112,42 +112,14 @@ namespace Discord.WebSocket } /// - public async Task ConnectAsync() + public async Task StartAsync() { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await ConnectInternalAsync().ConfigureAwait(false); - } - catch - { - await DisconnectInternalAsync().ConfigureAwait(false); - throw; - } - finally { _connectionLock.Release(); } - } - private async Task ConnectInternalAsync() - { - await Task.WhenAll( - _shards.Select(x => x.ConnectAsync()) - ).ConfigureAwait(false); - - CurrentUser = _shards[0].CurrentUser; + await Task.WhenAll(_shards.Select(x => x.StartAsync())).ConfigureAwait(false); } /// - public async Task DisconnectAsync() + public async Task StopAsync() { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync().ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - private async Task DisconnectInternalAsync() - { - for (int i = 0; i < _shards.Length; i++) - await _shards[i].DisconnectAsync(); + await Task.WhenAll(_shards.Select(x => x.StopAsync())).ConfigureAwait(false); } public DiscordSocketClient GetShard(int id) @@ -334,9 +306,6 @@ namespace Discord.WebSocket } //IDiscordClient - Task IDiscordClient.ConnectAsync() - => ConnectAsync(); - async Task IDiscordClient.GetApplicationInfoAsync() => await GetApplicationInfoAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs index cbefd795c..7d680eaf2 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketApiClient.cs @@ -28,6 +28,7 @@ namespace Discord.API private CancellationTokenSource _connectCancelToken; private string _gatewayUrl; + private bool _isExplicitUrl; internal IWebSocketClient WebSocketClient { get; } @@ -38,6 +39,8 @@ namespace Discord.API : base(restClientProvider, userAgent, defaultRetryMode, serializer) { _gatewayUrl = url; + if (url != null) + _isExplicitUrl = true; WebSocketClient = webSocketProvider(); //WebSocketClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .NET Framework 4.6+) WebSocketClient.BinaryMessage += async (data, index, count) => @@ -52,7 +55,8 @@ namespace Discord.API using (var jsonReader = new JsonTextReader(reader)) { var msg = _serializer.Deserialize(jsonReader); - await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false); + if (msg != null) + await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false); } } }; @@ -62,7 +66,8 @@ namespace Discord.API using (var jsonReader = new JsonTextReader(reader)) { var msg = _serializer.Deserialize(jsonReader); - await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false); + if (msg != null) + await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false); } }; WebSocketClient.Closed += async ex => @@ -107,7 +112,7 @@ namespace Discord.API if (WebSocketClient != null) WebSocketClient.SetCancelToken(_connectCancelToken.Token); - if (_gatewayUrl == null) + if (!_isExplicitUrl) { var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false); _gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}"; @@ -118,7 +123,8 @@ namespace Discord.API } catch { - _gatewayUrl = null; //Uncache in case the gateway url changed + if (!_isExplicitUrl) + _gatewayUrl = null; //Uncache in case the gateway url changed await DisconnectInternalAsync().ConfigureAwait(false); throw; } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index c0608a868..092225376 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -17,29 +17,27 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using GameModel = Discord.API.Game; -using Discord.Net; namespace Discord.WebSocket { public partial class DiscordSocketClient : BaseDiscordClient, IDiscordClient { private readonly ConcurrentQueue _largeGuilds; - private readonly Logger _gatewayLogger; private readonly JsonSerializer _serializer; private readonly SemaphoreSlim _connectionGroupLock; private readonly DiscordSocketClient _parentClient; private readonly ConcurrentQueue _heartbeatTimes; + private readonly ConnectionManager _connection; + private readonly Logger _gatewayLogger; + private readonly SemaphoreSlim _stateLock; private string _sessionId; private int _lastSeq; private ImmutableDictionary _voiceRegions; - private TaskCompletionSource _connectTask; - private CancellationTokenSource _cancelToken, _reconnectCancelToken; - private Task _heartbeatTask, _guildDownloadTask, _reconnectTask; + private Task _heartbeatTask, _guildDownloadTask; private int _unavailableGuilds; private long _lastGuildAvailableTime, _lastMessageTime; private int _nextAudioId; - private bool _canReconnect; private DateTimeOffset? _statusSince; private RestApplication _applicationInfo; private ConcurrentHashSet _downloadUsersFor; @@ -59,7 +57,6 @@ namespace Discord.WebSocket internal int LargeThreshold { get; private set; } internal AudioMode AudioMode { get; private set; } internal ClientState State { get; private set; } - internal int ConnectionTimeout { get; private set; } internal UdpSocketProvider UdpSocketProvider { get; private set; } internal WebSocketProvider WebSocketProvider { get; private set; } internal bool AlwaysDownloadUsers { get; private set; } @@ -90,35 +87,28 @@ namespace Discord.WebSocket UdpSocketProvider = config.UdpSocketProvider; WebSocketProvider = config.WebSocketProvider; AlwaysDownloadUsers = config.AlwaysDownloadUsers; - ConnectionTimeout = config.ConnectionTimeout; State = new ClientState(0, 0); _downloadUsersFor = new ConcurrentHashSet(); _heartbeatTimes = new ConcurrentQueue(); + + _stateLock = new SemaphoreSlim(1, 1); + _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); + _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, + OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); _nextAudioId = 1; - _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : "Shard #" + ShardId); _connectionGroupLock = groupLock; _parentClient = parentClient; _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { - _gatewayLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); + _gatewayLogger.WarningAsync("Serializer Error", e.ErrorContext.Error).GetAwaiter().GetResult(); e.ErrorContext.Handled = true; }; ApiClient.SentGatewayMessage += async opCode => await _gatewayLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.ReceivedGatewayEvent += ProcessMessageAsync; - ApiClient.Disconnected += async ex => - { - if (ex != null) - { - await _gatewayLogger.WarningAsync($"Connection Closed", ex).ConfigureAwait(false); - await StartReconnectAsync(ex).ConfigureAwait(false); - } - else - await _gatewayLogger.WarningAsync($"Connection Closed").ConfigureAwait(false); - }; LeftGuild += async g => await _gatewayLogger.InfoAsync($"Left {g.Name}").ConfigureAwait(false); JoinedGuild += async g => await _gatewayLogger.InfoAsync($"Joined {g.Name}").ConfigureAwait(false); @@ -143,8 +133,16 @@ namespace Discord.WebSocket } private static API.DiscordSocketApiClient CreateApiClient(DiscordSocketConfig config) => new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordRestConfig.UserAgent, config.GatewayHost); + internal override void Dispose(bool disposing) + { + if (disposing) + { + StopAsync().GetAwaiter().GetResult(); + ApiClient.Dispose(); + } + } - protected override async Task OnLoginAsync(TokenType tokenType, string token) + internal override async Task OnLoginAsync(TokenType tokenType, string token) { if (_parentClient == null) { @@ -154,92 +152,49 @@ namespace Discord.WebSocket else _voiceRegions = _parentClient._voiceRegions; } - protected override async Task OnLogoutAsync() + internal override async Task OnLogoutAsync() { - if (ConnectionState != ConnectionState.Disconnected) - await DisconnectInternalAsync(null, false).ConfigureAwait(false); - + await StopAsync().ConfigureAwait(false); _applicationInfo = null; _voiceRegions = ImmutableDictionary.Create(); _downloadUsersFor.Clear(); } + + public async Task StartAsync() + => await _connection.StartAsync().ConfigureAwait(false); + public async Task StopAsync() + => await _connection.StopAsync().ConfigureAwait(false); - /// - public async Task ConnectAsync() + private async Task OnConnectingAsync() { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await ConnectInternalAsync(false).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - private async Task ConnectInternalAsync(bool isReconnecting) - { - if (LoginState != LoginState.LoggedIn) - throw new InvalidOperationException("Client is not logged in."); - - if (!isReconnecting && _reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) - _reconnectCancelToken.Cancel(); - - var state = ConnectionState; - if (state == ConnectionState.Connecting || state == ConnectionState.Connected) - await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); - if (_connectionGroupLock != null) - await _connectionGroupLock.WaitAsync().ConfigureAwait(false); + await _connectionGroupLock.WaitAsync(_connection.CancelToken).ConfigureAwait(false); try { - _canReconnect = true; - ConnectionState = ConnectionState.Connecting; - await _gatewayLogger.InfoAsync("Connecting").ConfigureAwait(false); - - try - { - var connectTask = new TaskCompletionSource(); - _connectTask = connectTask; - _cancelToken = new CancellationTokenSource(); - - //Abort connection on timeout - var _ = Task.Run(async () => - { - await Task.Delay(ConnectionTimeout).ConfigureAwait(false); - connectTask.TrySetException(new TimeoutException()); - }); - - await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); - await ApiClient.ConnectAsync().ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); - - if (_sessionId != null) - { - await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false); - await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); - } - else - { - await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); - await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); - } - - await _connectTask.Task.ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); + await ApiClient.ConnectAsync().ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); + await _connectedEvent.InvokeAsync().ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); - await SendStatusAsync().ConfigureAwait(false); - - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - ConnectionState = ConnectionState.Connected; - await _gatewayLogger.InfoAsync("Connected").ConfigureAwait(false); - - await ProcessUserDownloadsAsync(_downloadUsersFor.Select(x => GetGuild(x)) - .Where(x => x != null).ToImmutableArray()).ConfigureAwait(false); + if (_sessionId != null) + { + await _gatewayLogger.DebugAsync("Resuming").ConfigureAwait(false); + await ApiClient.SendResumeAsync(_sessionId, _lastSeq).ConfigureAwait(false); } - catch (Exception) + else { - await DisconnectInternalAsync(null, isReconnecting).ConfigureAwait(false); - throw; + await _gatewayLogger.DebugAsync("Identifying").ConfigureAwait(false); + await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); } + + //Wait for READY + await _connection.WaitAsync().ConfigureAwait(false); + + await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); + await SendStatusAsync().ConfigureAwait(false); + + await ProcessUserDownloadsAsync(_downloadUsersFor.Select(x => GetGuild(x)) + .Where(x => x != null).ToImmutableArray()).ConfigureAwait(false); } finally { @@ -250,41 +205,11 @@ namespace Discord.WebSocket } } } - /// - public async Task DisconnectAsync() - { - if (_connectTask?.TrySetCanceled() ?? false) return; - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await DisconnectInternalAsync(null, false).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - private async Task DisconnectInternalAsync(Exception ex, bool isReconnecting) + private async Task OnDisconnectingAsync(Exception ex) { - if (!isReconnecting) - { - _canReconnect = false; - _sessionId = null; - _lastSeq = 0; - - if (_reconnectCancelToken != null && !_reconnectCancelToken.IsCancellationRequested) - _reconnectCancelToken.Cancel(); - } - ulong guildId; - if (ConnectionState == ConnectionState.Disconnected) return; - ConnectionState = ConnectionState.Disconnecting; - await _gatewayLogger.InfoAsync("Disconnecting").ConfigureAwait(false); - - await _gatewayLogger.DebugAsync("Cancelling current tasks").ConfigureAwait(false); - //Signal tasks to complete - try { _cancelToken.Cancel(); } catch { } - await _gatewayLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); - //Disconnect from server await ApiClient.DisconnectAsync().ConfigureAwait(false); //Wait for tasks to complete @@ -294,8 +219,8 @@ namespace Discord.WebSocket await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; - long times; - while (_heartbeatTimes.TryDequeue(out times)) { } + long time; + while (_heartbeatTimes.TryDequeue(out time)) { } _lastMessageTime = 0; await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); @@ -315,70 +240,6 @@ namespace Discord.WebSocket if (guild._available) await _guildUnavailableEvent.InvokeAsync(guild).ConfigureAwait(false); } - - ConnectionState = ConnectionState.Disconnected; - await _gatewayLogger.InfoAsync("Disconnected").ConfigureAwait(false); - - await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false); - } - - private async Task StartReconnectAsync(Exception ex) - { - if ((ex as WebSocketClosedException)?.CloseCode == 4004) //Bad Token - { - _canReconnect = false; - _connectTask?.TrySetException(ex); - await LogoutAsync().ConfigureAwait(false); - return; - } - - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - if (!_canReconnect || _reconnectTask != null) return; - _reconnectCancelToken = new CancellationTokenSource(); - _reconnectTask = ReconnectInternalAsync(ex, _reconnectCancelToken.Token); - } - finally { _connectionLock.Release(); } - } - private async Task ReconnectInternalAsync(Exception ex, CancellationToken cancelToken) - { - try - { - Random jitter = new Random(); - int nextReconnectDelay = 1000; - while (true) - { - await Task.Delay(nextReconnectDelay, cancelToken).ConfigureAwait(false); - nextReconnectDelay = nextReconnectDelay * 2 + jitter.Next(-250, 250); - if (nextReconnectDelay > 60000) - nextReconnectDelay = 60000; - - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - if (cancelToken.IsCancellationRequested) return; - await ConnectInternalAsync(true).ConfigureAwait(false); - _reconnectTask = null; - return; - } - catch (Exception ex2) - { - await _gatewayLogger.WarningAsync("Reconnect failed", ex2).ConfigureAwait(false); - } - finally { _connectionLock.Release(); } - } - } - catch (OperationCanceledException) - { - await _connectionLock.WaitAsync().ConfigureAwait(false); - try - { - await _gatewayLogger.DebugAsync("Reconnect cancelled").ConfigureAwait(false); - _reconnectTask = null; - } - finally { _connectionLock.Release(); } - } } /// @@ -555,7 +416,7 @@ namespace Discord.WebSocket await _gatewayLogger.DebugAsync("Received Hello").ConfigureAwait(false); var data = (payload as JToken).ToObject(_serializer); - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _cancelToken.Token, _gatewayLogger); + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); } break; case GatewayOpCode.Heartbeat: @@ -593,9 +454,7 @@ namespace Discord.WebSocket case GatewayOpCode.Reconnect: { await _gatewayLogger.DebugAsync("Received Reconnect").ConfigureAwait(false); - await _gatewayLogger.WarningAsync("Server requested a reconnect").ConfigureAwait(false); - - await StartReconnectAsync(new Exception("Server requested a reconnect")).ConfigureAwait(false); + _connection.Error(new Exception("Server requested a reconnect")); } break; case GatewayOpCode.Dispatch: @@ -633,8 +492,7 @@ namespace Discord.WebSocket } catch (Exception ex) { - _canReconnect = false; - _connectTask.TrySetException(new Exception("Processing READY failed", ex)); + _connection.CriticalError(new Exception("Processing READY failed", ex)); return; } @@ -642,11 +500,11 @@ namespace Discord.WebSocket await SyncGuildsAsync().ConfigureAwait(false); _lastGuildAvailableTime = Environment.TickCount; - _guildDownloadTask = WaitForGuildsAsync(_cancelToken.Token, _gatewayLogger); + _guildDownloadTask = WaitForGuildsAsync(_connection.CancelToken, _gatewayLogger); await _readyEvent.InvokeAsync().ConfigureAwait(false); - var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete + var _ = _connection.CompleteAsync(); await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); } break; @@ -654,7 +512,7 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false); - var _ = _connectTask.TrySetResultAsync(true); //Signal the .Connect() call to complete + var _ = _connection.CompleteAsync(); //Notify the client that these guilds are available again foreach (var guild in State.Guilds) @@ -1356,7 +1214,6 @@ namespace Discord.WebSocket SocketUserMessage cachedMsg = channel.GetCachedMessage(data.MessageId) as SocketUserMessage; var user = await channel.GetUserAsync(data.UserId, CacheMode.CacheOnly); SocketReaction reaction = SocketReaction.Create(data, channel, cachedMsg, Optional.Create(user)); - if (cachedMsg != null) { cachedMsg.AddReaction(reaction); @@ -1691,11 +1548,11 @@ namespace Discord.WebSocket } } - private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cancelToken, Logger logger) + private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cancelToken) { try { - await logger.DebugAsync("Heartbeat Started").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false); while (!cancelToken.IsCancellationRequested) { var now = Environment.TickCount; @@ -1705,8 +1562,7 @@ namespace Discord.WebSocket { if (ConnectionState == ConnectionState.Connected && (_guildDownloadTask?.IsCompleted ?? true)) { - await _gatewayLogger.WarningAsync("Server missed last heartbeat").ConfigureAwait(false); - await StartReconnectAsync(new Exception("Server missed last heartbeat")).ConfigureAwait(false); + _connection.Error(new Exception("Server missed last heartbeat")); return; } } @@ -1718,20 +1574,20 @@ namespace Discord.WebSocket } catch (Exception ex) { - await logger.WarningAsync("Heartbeat Errored", ex).ConfigureAwait(false); + await _gatewayLogger.WarningAsync("Heartbeat Errored", ex).ConfigureAwait(false); } await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); } - await logger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); } catch (OperationCanceledException) { - await logger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); + await _gatewayLogger.DebugAsync("Heartbeat Stopped").ConfigureAwait(false); } catch (Exception ex) { - await logger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); + await _gatewayLogger.ErrorAsync("Heartbeat Errored", ex).ConfigureAwait(false); } } public async Task WaitForGuildsAsync() @@ -1805,8 +1661,7 @@ namespace Discord.WebSocket } //IDiscordClient - Task IDiscordClient.ConnectAsync() - => ConnectAsync(); + ConnectionState IDiscordClient.ConnectionState => _connection.State; async Task IDiscordClient.GetApplicationInfoAsync() => await GetApplicationInfoAsync().ConfigureAwait(false); @@ -1842,5 +1697,10 @@ namespace Discord.WebSocket => Task.FromResult>(VoiceRegions); Task IDiscordClient.GetVoiceRegionAsync(string id) => Task.FromResult(GetVoiceRegion(id)); + + async Task IDiscordClient.StartAsync() + => await StartAsync().ConfigureAwait(false); + async Task IDiscordClient.StopAsync() + => await StopAsync().ConfigureAwait(false); } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs index 78a637d0e..f42744c79 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketConfig.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketConfig.cs @@ -9,7 +9,7 @@ namespace Discord.WebSocket { public const string GatewayEncoding = "json"; - /// Gets or sets the websocket host to connect to. If null, the client will use the /gateway endpoint. + /// Gets or sets the websocket host to connect to. If null, the client will use the /gateway endpoint. public string GatewayHost { get; set; } = null; /// Gets or sets the time, in milliseconds, to wait for a connection to complete before aborting. diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 22a4c2a71..4e16985a7 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -501,7 +501,7 @@ namespace Discord.WebSocket _audioConnectPromise?.TrySetCanceledAsync(); //Cancel any previous audio connection _audioConnectPromise = null; if (_audioClient != null) - await _audioClient.DisconnectAsync().ConfigureAwait(false); + await _audioClient.StopAsync().ConfigureAwait(false); _audioClient = null; } internal async Task FinishConnectAudio(int id, string url, string token) @@ -517,7 +517,6 @@ namespace Discord.WebSocket var promise = _audioConnectPromise; audioClient.Disconnected += async ex => { - //If the initial connection hasn't been made yet, reconnecting will lead to deadlocks if (!promise.Task.IsCompleted) { try { audioClient.Dispose(); } catch { } @@ -528,41 +527,15 @@ namespace Discord.WebSocket await promise.TrySetCanceledAsync(); return; } - - //TODO: Implement reconnect - /*await _audioLock.WaitAsync().ConfigureAwait(false); - try - { - if (AudioClient == audioClient) //Only reconnect if we're still assigned as this guild's audio client - { - if (ex != null) - { - //Reconnect if we still have channel info. - //TODO: Is this threadsafe? Could channel data be deleted before we access it? - var voiceState2 = GetVoiceState(Discord.CurrentUser.Id); - if (voiceState2.HasValue) - { - var voiceChannelId = voiceState2.Value.VoiceChannel?.Id; - if (voiceChannelId != null) - { - await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, voiceChannelId, voiceState2.Value.IsSelfDeafened, voiceState2.Value.IsSelfMuted); - return; - } - } - } - try { audioClient.Dispose(); } catch { } - AudioClient = null; - } - } - finally - { - _audioLock.Release(); - }*/ }; _audioClient = audioClient; } - await _audioClient.ConnectAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); - await _audioConnectPromise.TrySetResultAsync(_audioClient).ConfigureAwait(false); + _audioClient.Connected += () => + { + var _ = _audioConnectPromise.TrySetResultAsync(_audioClient); + return Task.Delay(0); + }; + await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); } catch (OperationCanceledException) { From 06dcac6a9f62dc68c60cea9dbdd58427223d7ede Mon Sep 17 00:00:00 2001 From: RogueException Date: Sat, 25 Feb 2017 16:06:42 -0400 Subject: [PATCH 255/263] Fixed audio and a few ConnectionManager issues --- src/Discord.Net.Rpc/Discord.Net.Rpc.csproj | 3 ++ .../DiscordRpcClient.Events.cs | 4 +-- src/Discord.Net.Rpc/DiscordRpcClient.cs | 2 ++ .../Audio/AudioClient.cs | 35 +++++++++++-------- .../ConnectionManager.cs | 11 ++++-- .../DiscordSocketClient.cs | 6 ++-- .../Entities/Guilds/SocketGuild.cs | 5 +-- 7 files changed, 43 insertions(+), 23 deletions(-) rename src/{Discord.Net.Rest => Discord.Net.WebSocket}/ConnectionManager.cs (95%) diff --git a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj index 628c8e032..85c2bf4e0 100644 --- a/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj +++ b/src/Discord.Net.Rpc/Discord.Net.Rpc.csproj @@ -19,6 +19,9 @@ Net\DefaultWebSocketClient.cs + + ConnectionManager.cs + diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs index d3c50a5ec..2a9ae21bf 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.Events.cs @@ -12,12 +12,12 @@ namespace Discord.Rpc remove { _connectedEvent.Remove(value); } } private readonly AsyncEvent> _connectedEvent = new AsyncEvent>(); - public event Func Disconnected + public event Func Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } - private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); + private readonly AsyncEvent> _disconnectedEvent = new AsyncEvent>(); public event Func Ready { add { _readyEvent.Add(value); } diff --git a/src/Discord.Net.Rpc/DiscordRpcClient.cs b/src/Discord.Net.Rpc/DiscordRpcClient.cs index 5235c98d4..01d641204 100644 --- a/src/Discord.Net.Rpc/DiscordRpcClient.cs +++ b/src/Discord.Net.Rpc/DiscordRpcClient.cs @@ -40,6 +40,8 @@ namespace Discord.Rpc _rpcLogger = LogManager.CreateLogger("RPC"); _connection = new ConnectionManager(_stateLock, _rpcLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 5404227f2..30073baeb 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -59,28 +59,28 @@ namespace Discord.Audio internal AudioClient(SocketGuild guild, int id) { Guild = guild; + _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); + + ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); + ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); + ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); + //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); + ApiClient.ReceivedEvent += ProcessMessageAsync; + ApiClient.ReceivedPacket += ProcessPacketAsync; _stateLock = new SemaphoreSlim(1, 1); _connection = new ConnectionManager(_stateLock, _audioLogger, 30000, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _heartbeatTimes = new ConcurrentQueue(); - - _audioLogger = Discord.LogManager.CreateLogger($"Audio #{id}"); _serializer = new JsonSerializer { ContractResolver = new DiscordContractResolver() }; _serializer.Error += (s, e) => { _audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); e.ErrorContext.Handled = true; - }; - - ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); - - ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); - ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false); - //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); - ApiClient.ReceivedEvent += ProcessMessageAsync; - ApiClient.ReceivedPacket += ProcessPacketAsync; + }; LatencyUpdated += async (old, val) => await _audioLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); } @@ -98,25 +98,32 @@ namespace Discord.Audio private async Task OnConnectingAsync() { + await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false); + await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); + + //Wait for READY + await _connection.WaitAsync().ConfigureAwait(false); } private async Task OnDisconnectingAsync(Exception ex) { - //Disconnect from server + await _audioLogger.DebugAsync("Disconnecting ApiClient").ConfigureAwait(false); await ApiClient.DisconnectAsync().ConfigureAwait(false); //Wait for tasks to complete + await _audioLogger.DebugAsync("Waiting for heartbeater").ConfigureAwait(false); var heartbeatTask = _heartbeatTask; if (heartbeatTask != null) await heartbeatTask.ConfigureAwait(false); _heartbeatTask = null; - await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); - long time; while (_heartbeatTimes.TryDequeue(out time)) { } _lastMessageTime = 0; + + await _audioLogger.DebugAsync("Sending Voice State").ConfigureAwait(false); + await Discord.ApiClient.SendVoiceStateUpdateAsync(Guild.Id, null, false, false).ConfigureAwait(false); } public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis) diff --git a/src/Discord.Net.Rest/ConnectionManager.cs b/src/Discord.Net.WebSocket/ConnectionManager.cs similarity index 95% rename from src/Discord.Net.Rest/ConnectionManager.cs rename to src/Discord.Net.WebSocket/ConnectionManager.cs index ab1f4790c..72926e2e3 100644 --- a/src/Discord.Net.Rest/ConnectionManager.cs +++ b/src/Discord.Net.WebSocket/ConnectionManager.cs @@ -2,6 +2,7 @@ using Discord.Logging; using System; using System.Threading; using System.Threading.Tasks; +using Discord.Net; namespace Discord { @@ -39,7 +40,13 @@ namespace Discord clientDisconnectHandler(ex => { if (ex != null) - Error(new Exception("WebSocket connection was closed", ex)); + { + var ex2 = ex as WebSocketClosedException; + if (ex2?.CloseCode == 4006) + CriticalError(new Exception("WebSocket session expired", ex)); + else + Error(new Exception("WebSocket connection was closed", ex)); + } else Error(new Exception("WebSocket connection was closed")); return Task.Delay(0); @@ -50,7 +57,7 @@ namespace Discord { await AcquireConnectionLock().ConfigureAwait(false); var reconnectCancelToken = new CancellationTokenSource(); - _reconnectCancelToken = new CancellationTokenSource(); + _reconnectCancelToken = reconnectCancelToken; _task = Task.Run(async () => { try diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 092225376..d52de0af1 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -95,6 +95,8 @@ namespace Discord.WebSocket _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); + _connection.Connected += () => _connectedEvent.InvokeAsync(); + _connection.Disconnected += (ex, recon) => _disconnectedEvent.InvokeAsync(ex); _nextAudioId = 1; _connectionGroupLock = groupLock; @@ -173,8 +175,6 @@ namespace Discord.WebSocket { await _gatewayLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); await ApiClient.ConnectAsync().ConfigureAwait(false); - await _gatewayLogger.DebugAsync("Raising Event").ConfigureAwait(false); - await _connectedEvent.InvokeAsync().ConfigureAwait(false); if (_sessionId != null) { @@ -189,7 +189,7 @@ namespace Discord.WebSocket //Wait for READY await _connection.WaitAsync().ConfigureAwait(false); - + await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); await SendStatusAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 4e16985a7..007f52124 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -506,15 +506,16 @@ namespace Discord.WebSocket } internal async Task FinishConnectAudio(int id, string url, string token) { + //TODO: Mem Leak: Disconnected/Connected handlers arent cleaned up var voiceState = GetVoiceState(Discord.CurrentUser.Id).Value; await _audioLock.WaitAsync().ConfigureAwait(false); try { + var promise = _audioConnectPromise; if (_audioClient == null) { var audioClient = new AudioClient(this, id); - var promise = _audioConnectPromise; audioClient.Disconnected += async ex => { if (!promise.Task.IsCompleted) @@ -532,7 +533,7 @@ namespace Discord.WebSocket } _audioClient.Connected += () => { - var _ = _audioConnectPromise.TrySetResultAsync(_audioClient); + var _ = promise.TrySetResultAsync(_audioClient); return Task.Delay(0); }; await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); From 2db60749cabad598fdab79bee64dae9ac1cf3bca Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 25 Feb 2017 16:17:58 -0500 Subject: [PATCH 256/263] Add IsUnique parameter to CreateChannelInvite Resolves #469 --- src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs | 2 +- .../API/Rest/CreateChannelInviteParams.cs | 2 ++ src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 4 ++-- .../Entities/Channels/RestGuildChannel.cs | 8 ++++---- src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs | 8 ++++---- .../Entities/Channels/SocketGuildChannel.cs | 8 ++++---- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index ee47a7280..3d08a8c51 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -20,7 +20,7 @@ namespace Discord /// The time (in seconds) until the invite expires. Set to null to never expire. /// The max amount of times this invite may be used. Set to null to have unlimited uses. /// If true, a user accepting this invite will be kicked from the guild after closing their client. - Task CreateInviteAsync(int? maxAge = 1800, int? maxUses = default(int?), bool isTemporary = false, RequestOptions options = null); + 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); diff --git a/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs b/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs index cc3ca6dca..db79bc314 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateChannelInviteParams.cs @@ -12,5 +12,7 @@ namespace Discord.API.Rest public Optional MaxUses { get; set; } [JsonProperty("temporary")] public Optional IsTemporary { get; set; } + [JsonProperty("unique")] + public Optional IsUnique { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 2036f8824..07bdfe0eb 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -69,9 +69,9 @@ namespace Discord.Rest return models.Select(x => RestInviteMetadata.Create(client, null, channel, x)).ToImmutableArray(); } public static async Task CreateInviteAsync(IGuildChannel channel, BaseDiscordClient client, - int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) + int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) { - var args = new CreateChannelInviteParams { IsTemporary = isTemporary }; + var args = new CreateChannelInviteParams { IsTemporary = isTemporary, IsUnique = isUnique }; if (maxAge.HasValue) args.MaxAge = maxAge.Value; else diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index edb751133..114c886c4 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -118,8 +118,8 @@ namespace Discord.Rest public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) - => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public override string ToString() => Name; @@ -136,8 +136,8 @@ namespace Discord.Rest async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); - async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) - => await CreateInviteAsync(maxAge, maxUses, isTemporary, 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) => GetPermissionOverwrite(role); diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs index 05feac03e..48eb8ec3e 100644 --- a/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs +++ b/src/Discord.Net.Rpc/Entities/Channels/RpcGuildChannel.cs @@ -51,8 +51,8 @@ namespace Discord.Rpc public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) - => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public override string ToString() => Name; @@ -68,8 +68,8 @@ namespace Discord.Rpc async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); - async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) - => await CreateInviteAsync(maxAge, maxUses, isTemporary, 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); IReadOnlyCollection IGuildChannel.PermissionOverwrites { get { throw new NotSupportedException(); } } OverwritePermissions? IGuildChannel.GetPermissionOverwrite(IUser user) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 6a56c97fe..0e7cfde82 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -112,8 +112,8 @@ namespace Discord.WebSocket public async Task> GetInvitesAsync(RequestOptions options = null) => await ChannelHelper.GetInvitesAsync(this, Discord, options).ConfigureAwait(false); - public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, RequestOptions options = null) - => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, options).ConfigureAwait(false); + public async Task CreateInviteAsync(int? maxAge = 3600, int? maxUses = null, bool isTemporary = false, bool isUnique = false, RequestOptions options = null) + => await ChannelHelper.CreateInviteAsync(this, Discord, maxAge, maxUses, isTemporary, isUnique, options).ConfigureAwait(false); public new abstract SocketGuildUser GetUser(ulong id); @@ -130,8 +130,8 @@ namespace Discord.WebSocket async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); - async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, RequestOptions options) - => await CreateInviteAsync(maxAge, maxUses, isTemporary, 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) => GetPermissionOverwrite(role); From 4a18b321eaabf65a3c0e6ae90b67274ff46f6ba7 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sat, 25 Feb 2017 16:31:46 -0500 Subject: [PATCH 257/263] Change the minimum allowed message to 13d23h59m Connection/API lag could cause messages right on the edge of 14 weeks to fail on Discord's end when they pass our checks. --- src/Discord.Net.Core/Utils/Preconditions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index c246538d8..5c5cba7e6 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -185,7 +185,7 @@ namespace Discord // Bulk Delete public static void NoMessageOlderThanTwoWeeks(ulong[] collection, string name) { - var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.Now.Subtract(TimeSpan.FromDays(14))); + var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.Now.Subtract(TimeSpan.FromMilliseconds(1209540000))); for (var i = 0; i < collection.Length; i++) if (collection[i] <= minimum) throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks to delete."); } From 8e0c65498b3b2225e98c6ff613f44a93da66aa45 Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 26 Feb 2017 10:57:28 -0400 Subject: [PATCH 258/263] Cleaned up audio code --- .../Audio/AudioApplication.cs | 9 + src/Discord.Net.Core/Audio/AudioInStream.cs | 24 ++- src/Discord.Net.Core/Audio/AudioOutStream.cs | 31 +++- src/Discord.Net.Core/Audio/IAudioClient.cs | 4 +- .../Audio/AudioClient.cs | 31 ++-- .../Audio/Opus/OpusApplication.cs | 2 +- .../Audio/Opus/OpusCtl.cs | 10 +- .../Audio/Opus/OpusEncoder.cs | 70 +++++--- .../Audio/Opus/OpusSignal.cs | 9 + .../Audio/Streams/BufferedWriteStream.cs | 156 ++++++++++++++++++ .../Audio/Streams/OpusDecodeStream.cs | 24 ++- .../Audio/Streams/OpusEncodeStream.cs | 39 +++-- .../Audio/Streams/OutputStream.cs | 23 +++ .../Audio/Streams/RTPReadStream.cs | 16 +- .../Audio/Streams/RTPWriteStream.cs | 75 +++------ .../Audio/Streams/SodiumDecryptStream.cs | 42 +++++ .../Audio/Streams/SodiumEncryptStream.cs | 45 +++++ .../Audio/Targets/BufferedAudioTarget.cs | 119 ------------- .../Audio/Targets/DirectAudioTarget.cs | 22 --- .../Audio/Targets/IAudioTarget.cs | 12 -- .../DiscordVoiceApiClient.cs | 8 +- 21 files changed, 495 insertions(+), 276 deletions(-) create mode 100644 src/Discord.Net.Core/Audio/AudioApplication.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Opus/OpusSignal.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Streams/OutputStream.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs delete mode 100644 src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs delete mode 100644 src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs delete mode 100644 src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs diff --git a/src/Discord.Net.Core/Audio/AudioApplication.cs b/src/Discord.Net.Core/Audio/AudioApplication.cs new file mode 100644 index 000000000..276d934b2 --- /dev/null +++ b/src/Discord.Net.Core/Audio/AudioApplication.cs @@ -0,0 +1,9 @@ +namespace Discord.Audio +{ + public enum AudioApplication : int + { + Voice, + Music, + Mixed + } +} \ No newline at end of file diff --git a/src/Discord.Net.Core/Audio/AudioInStream.cs b/src/Discord.Net.Core/Audio/AudioInStream.cs index e6c9c4b04..4023f9c86 100644 --- a/src/Discord.Net.Core/Audio/AudioInStream.cs +++ b/src/Discord.Net.Core/Audio/AudioInStream.cs @@ -1,8 +1,30 @@ -using System.IO; +using System; +using System.IO; +using System.Threading; namespace Discord.Audio { public abstract class AudioInStream : Stream { + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + + public override void Flush() { throw new NotSupportedException(); } + + public override long Length { get { throw new NotSupportedException(); } } + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override void SetLength(long value) { throw new NotSupportedException(); } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } } } diff --git a/src/Discord.Net.Core/Audio/AudioOutStream.cs b/src/Discord.Net.Core/Audio/AudioOutStream.cs index dd91b71ee..2b4b012ee 100644 --- a/src/Discord.Net.Core/Audio/AudioOutStream.cs +++ b/src/Discord.Net.Core/Audio/AudioOutStream.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using System.Threading; using System.Threading.Tasks; @@ -10,7 +11,31 @@ namespace Discord.Audio public override bool CanSeek => false; public override bool CanWrite => true; - public virtual void Clear() { } - public virtual Task ClearAsync(CancellationToken cancelToken) { return Task.Delay(0); } + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + public override void Flush() + { + FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + public void Clear() + { + ClearAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + + public virtual Task ClearAsync(CancellationToken cancellationToken) { return Task.Delay(0); } + //public virtual Task WriteSilenceAsync(CancellationToken cancellationToken) { return Task.Delay(0); } + + public override long Length { get { throw new NotSupportedException(); } } + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + public override void SetLength(long value) { throw new NotSupportedException(); } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } } } diff --git a/src/Discord.Net.Core/Audio/IAudioClient.cs b/src/Discord.Net.Core/Audio/IAudioClient.cs index 4a6ae2e27..bea44fcf4 100644 --- a/src/Discord.Net.Core/Audio/IAudioClient.cs +++ b/src/Discord.Net.Core/Audio/IAudioClient.cs @@ -34,13 +34,13 @@ namespace Discord.Audio /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// /// - AudioOutStream CreatePCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000); + AudioOutStream CreatePCMStream(AudioApplication application, int samplesPerFrame, int channels = 2, int? bitrate = null, int bufferMillis = 1000); /// /// Creates a new direct outgoing stream accepting PCM (raw) data. This is a direct stream with no internal timer. /// /// Samples per frame. Must be 120, 240, 480, 960, 1920 or 2880, representing 2.5, 5, 10, 20, 40 or 60 milliseconds respectively. /// /// - AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels = 2, int? bitrate = null); + AudioOutStream CreateDirectPCMStream(AudioApplication application, int samplesPerFrame, int channels = 2, int? bitrate = null); } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 30073baeb..7645604df 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -1,4 +1,5 @@ using Discord.API.Voice; +using Discord.Audio.Streams; using Discord.Logging; using Discord.Net.Converters; using Discord.WebSocket; @@ -80,7 +81,7 @@ namespace Discord.Audio { _audioLogger.WarningAsync(e.ErrorContext.Error).GetAwaiter().GetResult(); e.ErrorContext.Handled = true; - }; + }; LatencyUpdated += async (old, val) => await _audioLogger.VerboseAsync($"Latency = {val} ms").ConfigureAwait(false); } @@ -129,26 +130,34 @@ namespace Discord.Audio public AudioOutStream CreateOpusStream(int samplesPerFrame, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _connection.CancelToken); - return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); + var outputStream = new OutputStream(ApiClient); + var sodiumEncrypter = new SodiumEncryptStream(outputStream, _secretKey); + var rtpWriter = new RTPWriteStream(sodiumEncrypter, samplesPerFrame, _ssrc); + return new BufferedWriteStream(rtpWriter, samplesPerFrame, bufferMillis, _connection.CancelToken, _audioLogger); } public AudioOutStream CreateDirectOpusStream(int samplesPerFrame) { CheckSamplesPerFrame(samplesPerFrame); - var target = new DirectAudioTarget(ApiClient); - return new RTPWriteStream(target, _secretKey, samplesPerFrame, _ssrc); + var outputStream = new OutputStream(ApiClient); + var sodiumEncrypter = new SodiumEncryptStream(outputStream, _secretKey); + return new RTPWriteStream(sodiumEncrypter, samplesPerFrame, _ssrc); } - public AudioOutStream CreatePCMStream(int samplesPerFrame, int channels, int? bitrate, int bufferMillis) + public AudioOutStream CreatePCMStream(AudioApplication application, int samplesPerFrame, int channels, int? bitrate, int bufferMillis) { CheckSamplesPerFrame(samplesPerFrame); - var target = new BufferedAudioTarget(ApiClient, samplesPerFrame, bufferMillis, _connection.CancelToken); - return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); + var outputStream = new OutputStream(ApiClient); + var sodiumEncrypter = new SodiumEncryptStream(outputStream, _secretKey); + var rtpWriter = new RTPWriteStream(sodiumEncrypter, samplesPerFrame, _ssrc); + var bufferedStream = new BufferedWriteStream(rtpWriter, samplesPerFrame, bufferMillis, _connection.CancelToken, _audioLogger); + return new OpusEncodeStream(bufferedStream, channels, samplesPerFrame, bitrate ?? (96 * 1024), application); } - public AudioOutStream CreateDirectPCMStream(int samplesPerFrame, int channels, int? bitrate) + public AudioOutStream CreateDirectPCMStream(AudioApplication application, int samplesPerFrame, int channels, int? bitrate) { CheckSamplesPerFrame(samplesPerFrame); - var target = new DirectAudioTarget(ApiClient); - return new OpusEncodeStream(target, _secretKey, channels, samplesPerFrame, _ssrc, bitrate); + var outputStream = new OutputStream(ApiClient); + var sodiumEncrypter = new SodiumEncryptStream(outputStream, _secretKey); + var rtpWriter = new RTPWriteStream(sodiumEncrypter, samplesPerFrame, _ssrc); + return new OpusEncodeStream(rtpWriter, channels, samplesPerFrame, bitrate ?? (96 * 1024), application); } private void CheckSamplesPerFrame(int samplesPerFrame) { diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs index d6a3ce0cf..e288bb626 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusApplication.cs @@ -1,6 +1,6 @@ namespace Discord.Audio { - public enum OpusApplication : int + internal enum OpusApplication : int { Voice = 2048, MusicOrMixed = 2049, diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs index e71213ae6..0b6a4e37f 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusCtl.cs @@ -1,10 +1,12 @@ namespace Discord.Audio { + //https://github.com/gcp/opus/blob/master/include/opus_defines.h internal enum OpusCtl : int { - SetBitrateRequest = 4002, - GetBitrateRequest = 4003, - SetInbandFECRequest = 4012, - GetInbandFECRequest = 4013 + SetBitrate = 4002, + SetBandwidth = 4008, + SetInbandFEC = 4012, + SetPacketLossPercent = 4014, + SetSignal = 4024 } } diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs index 2cb3949a9..ca87c5fc6 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs @@ -15,17 +15,62 @@ namespace Discord.Audio private static extern int EncoderCtl(IntPtr st, OpusCtl request, int value); /// Gets the coding mode of the encoder. - public OpusApplication Application { get; } + public AudioApplication Application { get; } + public int BitRate { get;} - public OpusEncoder(int samplingRate, int channels, OpusApplication application = OpusApplication.MusicOrMixed) + public OpusEncoder(int samplingRate, int channels, int bitrate, AudioApplication application) : base(samplingRate, channels) { + if (bitrate < 1 || bitrate > DiscordVoiceAPIClient.MaxBitrate) + throw new ArgumentOutOfRangeException(nameof(bitrate)); + Application = application; + BitRate = bitrate; + + OpusApplication opusApplication; + OpusSignal opusSignal; + switch (application) + { + case AudioApplication.Mixed: + opusApplication = OpusApplication.MusicOrMixed; + opusSignal = OpusSignal.Auto; + break; + case AudioApplication.Music: + opusApplication = OpusApplication.MusicOrMixed; + opusSignal = OpusSignal.Music; + break; + case AudioApplication.Voice: + opusApplication = OpusApplication.Voice; + opusSignal = OpusSignal.Voice; + break; + default: + throw new ArgumentOutOfRangeException(nameof(application)); + } OpusError error; - _ptr = CreateEncoder(samplingRate, channels, (int)application, out error); + _ptr = CreateEncoder(samplingRate, channels, (int)opusApplication, out error); if (error != OpusError.OK) throw new Exception($"Opus Error: {error}"); + + var result = EncoderCtl(_ptr, OpusCtl.SetSignal, (int)opusSignal); + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}"); + + result = EncoderCtl(_ptr, OpusCtl.SetPacketLossPercent, 5); //%% + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}"); + + result = EncoderCtl(_ptr, OpusCtl.SetInbandFEC, 1); //True + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}"); + + result = EncoderCtl(_ptr, OpusCtl.SetBitrate, bitrate); + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}"); + + /*result = EncoderCtl(_ptr, OpusCtl.SetBandwidth, 1105); + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}");*/ } /// Produces Opus encoded audio from PCM samples. @@ -44,25 +89,6 @@ namespace Discord.Audio return result; } - /// Gets or sets whether Forward Error Correction is enabled. - public void SetForwardErrorCorrection(bool value) - { - var result = EncoderCtl(_ptr, OpusCtl.SetInbandFECRequest, value ? 1 : 0); - if (result < 0) - throw new Exception($"Opus Error: {(OpusError)result}"); - } - - /// Gets or sets the encoder's bitrate. - public void SetBitrate(int value) - { - if (value < 1 || value > DiscordVoiceAPIClient.MaxBitrate) - throw new ArgumentOutOfRangeException(nameof(value)); - - var result = EncoderCtl(_ptr, OpusCtl.SetBitrateRequest, value); - if (result < 0) - throw new Exception($"Opus Error: {(OpusError)result}"); - } - protected override void Dispose(bool disposing) { if (_ptr != IntPtr.Zero) diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusSignal.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusSignal.cs new file mode 100644 index 000000000..3f95183f4 --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusSignal.cs @@ -0,0 +1,9 @@ +namespace Discord.Audio +{ + internal enum OpusSignal : int + { + Auto = -1000, + Voice = 3001, + Music = 3002, + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs new file mode 100644 index 000000000..f12417d4e --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs @@ -0,0 +1,156 @@ +using Discord.Logging; +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio.Streams +{ + /// Wraps another stream with a timed buffer. + public class BufferedWriteStream : AudioOutStream + { + private struct Frame + { + public Frame(byte[] buffer, int bytes) + { + Buffer = buffer; + Bytes = bytes; + } + + public readonly byte[] Buffer; + public readonly int Bytes; + } + + private static readonly byte[] _silenceFrame = new byte[0]; + + private readonly AudioOutStream _next; + private readonly CancellationTokenSource _cancelTokenSource; + private readonly CancellationToken _cancelToken; + private readonly Task _task; + private readonly ConcurrentQueue _queuedFrames; + private readonly ConcurrentQueue _bufferPool; + private readonly SemaphoreSlim _queueLock; + private readonly Logger _logger; + private readonly int _ticksPerFrame, _queueLength; + private bool _isPreloaded; + + internal BufferedWriteStream(AudioOutStream next, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken, Logger logger, int maxFrameSize = 1500) + { + //maxFrameSize = 1275 was too limiting at 128*1024 + _next = next; + _ticksPerFrame = samplesPerFrame / 48; + _logger = logger; + _queueLength = (bufferMillis + (_ticksPerFrame - 1)) / _ticksPerFrame; //Round up + + _cancelTokenSource = new CancellationTokenSource(); + _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; + _queuedFrames = new ConcurrentQueue(); + _bufferPool = new ConcurrentQueue(); + for (int i = 0; i < _queueLength; i++) + _bufferPool.Enqueue(new byte[maxFrameSize]); + _queueLock = new SemaphoreSlim(_queueLength, _queueLength); + + _task = Run(); + } + + private Task Run() + { + uint num = 0; + return Task.Run(async () => + { + try + { + while (!_isPreloaded && !_cancelToken.IsCancellationRequested) + await Task.Delay(1).ConfigureAwait(false); + + long nextTick = Environment.TickCount; + while (!_cancelToken.IsCancellationRequested) + { + const int limit = 1; + long tick = Environment.TickCount; + long dist = nextTick - tick; + if (dist <= limit) + { + Frame frame; + if (_queuedFrames.TryDequeue(out frame)) + { + await _next.WriteAsync(frame.Buffer, 0, frame.Bytes).ConfigureAwait(false); + _bufferPool.Enqueue(frame.Buffer); + _queueLock.Release(); + nextTick += _ticksPerFrame; +#if DEBUG + var _ = _logger.DebugAsync($"{num++}: Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)"); +#endif + } + else if (dist == 0) + { + await _next.WriteAsync(_silenceFrame, 0, _silenceFrame.Length).ConfigureAwait(false); + nextTick += _ticksPerFrame; +#if DEBUG + var _ = _logger.DebugAsync($"{num++}: Buffer underrun"); +#endif + } + } + else + await Task.Delay((int)(dist - (limit - 1))).ConfigureAwait(false); + } + } + catch (OperationCanceledException) { } + }); + } + + public override async Task WriteAsync(byte[] data, int offset, int count, CancellationToken cancelToken) + { + if (cancelToken.CanBeCanceled) + cancelToken = CancellationTokenSource.CreateLinkedTokenSource(cancelToken, _cancelToken).Token; + else + cancelToken = _cancelToken; + + await _queueLock.WaitAsync(-1, cancelToken).ConfigureAwait(false); + byte[] buffer; + if (!_bufferPool.TryDequeue(out buffer)) + { +#if DEBUG + var _ = _logger.DebugAsync($"Buffer overflow"); //Should never happen because of the queueLock +#endif + return; + } + Buffer.BlockCopy(data, offset, buffer, 0, count); + _queuedFrames.Enqueue(new Frame(buffer, count)); +#if DEBUG + //var _ await _logger.DebugAsync($"Queued {count} bytes ({_queuedFrames.Count} frames buffered)"); +#endif + if (!_isPreloaded && _queuedFrames.Count == _queueLength) + { +#if DEBUG + var _ = _logger.DebugAsync($"Preloaded"); +#endif + _isPreloaded = true; + } + } + + public override async Task FlushAsync(CancellationToken cancelToken) + { + while (true) + { + cancelToken.ThrowIfCancellationRequested(); + if (_queuedFrames.Count == 0) + return; + await Task.Delay(250, cancelToken).ConfigureAwait(false); + } + } + public override Task ClearAsync(CancellationToken cancelToken) + { + Frame ignored; + do + cancelToken.ThrowIfCancellationRequested(); + while (_queuedFrames.TryDequeue(out ignored)); + return Task.Delay(0); + } + protected override void Dispose(bool disposing) + { + if (disposing) + _cancelTokenSource.Cancel(); + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs index 3a650eeaf..c700a7f15 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs @@ -1,22 +1,34 @@ -namespace Discord.Audio +using System; +using System.Collections.Concurrent; + +namespace Discord.Audio.Streams { - internal class OpusDecodeStream : RTPReadStream + /// Converts Opus to PCM + public class OpusDecodeStream : AudioInStream { + private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer private readonly byte[] _buffer; private readonly OpusDecoder _decoder; - internal OpusDecodeStream(AudioClient audioClient, byte[] secretKey, int samplingRate, - int channels = OpusConverter.MaxChannels, int bufferSize = 4000) - : base(audioClient, secretKey) + internal OpusDecodeStream(AudioClient audioClient, int samplingRate, int channels = OpusConverter.MaxChannels, int bufferSize = 4000) { _buffer = new byte[bufferSize]; _decoder = new OpusDecoder(samplingRate, channels); + _queuedData = new BlockingCollection(100); } public override int Read(byte[] buffer, int offset, int count) + { + var queuedData = _queuedData.Take(); + Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); + return queuedData.Length; + } + public override void Write(byte[] buffer, int offset, int count) { count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0); - return base.Read(_buffer, 0, count); + var newBuffer = new byte[count]; + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, count); + _queuedData.Add(newBuffer); } protected override void Dispose(bool disposing) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index 570c4e73c..01747fc05 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -2,27 +2,28 @@ using System.Threading; using System.Threading.Tasks; -namespace Discord.Audio +namespace Discord.Audio.Streams { - internal class OpusEncodeStream : RTPWriteStream + /// Converts PCM to Opus + public class OpusEncodeStream : AudioOutStream { public const int SampleRate = 48000; + + private readonly AudioOutStream _next; + private readonly OpusEncoder _encoder; + private readonly byte[] _buffer; + private int _frameSize; private byte[] _partialFrameBuffer; private int _partialFramePos; - private readonly OpusEncoder _encoder; - - internal OpusEncodeStream(IAudioTarget target, byte[] secretKey, int channels, int samplesPerFrame, uint ssrc, int? bitrate = null) - : base(target, secretKey, samplesPerFrame, ssrc) + internal OpusEncodeStream(AudioOutStream next, int channels, int samplesPerFrame, int bitrate, AudioApplication application, int bufferSize = 4000) { - _encoder = new OpusEncoder(SampleRate, channels); + _next = next; + _encoder = new OpusEncoder(SampleRate, channels, bitrate, application); _frameSize = samplesPerFrame * channels * 2; + _buffer = new byte[bufferSize]; _partialFrameBuffer = new byte[_frameSize]; - - _encoder.SetForwardErrorCorrection(true); - if (bitrate != null) - _encoder.SetBitrate(bitrate.Value); } public override void Write(byte[] buffer, int offset, int count) @@ -43,7 +44,7 @@ namespace Discord.Audio _partialFramePos = 0; int encFrameSize = _encoder.EncodeFrame(_partialFrameBuffer, 0, _frameSize, _buffer, 0); - await base.WriteAsync(_buffer, 0, encFrameSize, cancellationToken).ConfigureAwait(false); + await _next.WriteAsync(_buffer, 0, encFrameSize, cancellationToken).ConfigureAwait(false); } else { @@ -54,10 +55,7 @@ namespace Discord.Audio } } - /*public override void Flush() - { - FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); - } + /* public override async Task FlushAsync(CancellationToken cancellationToken) { try @@ -70,6 +68,15 @@ namespace Discord.Audio await base.FlushAsync(cancellationToken).ConfigureAwait(false); }*/ + public override async Task FlushAsync(CancellationToken cancelToken) + { + await _next.FlushAsync(cancelToken).ConfigureAwait(false); + } + public override async Task ClearAsync(CancellationToken cancelToken) + { + await _next.ClearAsync(cancelToken).ConfigureAwait(false); + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OutputStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OutputStream.cs new file mode 100644 index 000000000..6238e93b4 --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Streams/OutputStream.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio.Streams +{ + /// Wraps an IAudioClient, sending voice data on write. + public class OutputStream : AudioOutStream + { + private readonly DiscordVoiceAPIClient _client; + public OutputStream(IAudioClient client) + : this((client as AudioClient).ApiClient) { } + internal OutputStream(DiscordVoiceAPIClient client) + { + _client = client; + } + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) + { + cancelToken.ThrowIfCancellationRequested(); + await _client.SendAsync(buffer, offset, count).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs index cfc804abe..0cc7a1529 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs @@ -2,11 +2,13 @@ using System.Collections.Concurrent; using System.IO; -namespace Discord.Audio +namespace Discord.Audio.Streams { - internal class RTPReadStream : Stream + /// Reads the payload from an RTP frame + public class RTPReadStream : AudioInStream { private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer + //private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer private readonly AudioClient _audioClient; private readonly byte[] _buffer, _nonce, _secretKey; @@ -23,6 +25,12 @@ namespace Discord.Audio _nonce = new byte[24]; } + /*public RTPFrame ReadFrame() + { + var queuedData = _queuedData.Take(); + Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); + return queuedData.Length; + }*/ public override int Read(byte[] buffer, int offset, int count) { var queuedData = _queuedData.Take(); @@ -31,10 +39,8 @@ namespace Discord.Audio } public override void Write(byte[] buffer, int offset, int count) { - Buffer.BlockCopy(buffer, 0, _nonce, 0, 12); - count = SecretBox.Decrypt(buffer, offset, count, _buffer, 0, _nonce, _secretKey); var newBuffer = new byte[count]; - Buffer.BlockCopy(_buffer, 0, newBuffer, 0, count); + Buffer.BlockCopy(buffer, 0, newBuffer, 0, count); _queuedData.Add(newBuffer); } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 7ba95c591..5b8877f8e 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -1,33 +1,32 @@ using System; -using System.IO; using System.Threading; using System.Threading.Tasks; -namespace Discord.Audio +namespace Discord.Audio.Streams { - internal class RTPWriteStream : AudioOutStream + /// Wraps data in an RTP frame + public class RTPWriteStream : AudioOutStream { - private readonly IAudioTarget _target; - private readonly byte[] _nonce, _secretKey; + private readonly AudioOutStream _next; + private readonly byte[] _header; private int _samplesPerFrame; private uint _ssrc, _timestamp = 0; protected readonly byte[] _buffer; - internal RTPWriteStream(IAudioTarget target, byte[] secretKey, int samplesPerFrame, uint ssrc) + internal RTPWriteStream(AudioOutStream next, int samplesPerFrame, uint ssrc, int bufferSize = 4000) { - _target = target; - _secretKey = secretKey; + _next = next; _samplesPerFrame = samplesPerFrame; _ssrc = ssrc; - _buffer = new byte[4000]; - _nonce = new byte[24]; - _nonce[0] = 0x80; - _nonce[1] = 0x78; - _nonce[8] = (byte)(_ssrc >> 24); - _nonce[9] = (byte)(_ssrc >> 16); - _nonce[10] = (byte)(_ssrc >> 8); - _nonce[11] = (byte)(_ssrc >> 0); + _buffer = new byte[bufferSize]; + _header = new byte[24]; + _header[0] = 0x80; + _header[1] = 0x78; + _header[8] = (byte)(_ssrc >> 24); + _header[9] = (byte)(_ssrc >> 16); + _header[10] = (byte)(_ssrc >> 8); + _header[11] = (byte)(_ssrc >> 0); } public override void Write(byte[] buffer, int offset, int count) @@ -39,48 +38,28 @@ namespace Discord.Audio cancellationToken.ThrowIfCancellationRequested(); unchecked { - if (_nonce[3]++ == byte.MaxValue) - _nonce[2]++; + if (_header[3]++ == byte.MaxValue) + _header[2]++; _timestamp += (uint)_samplesPerFrame; - _nonce[4] = (byte)(_timestamp >> 24); - _nonce[5] = (byte)(_timestamp >> 16); - _nonce[6] = (byte)(_timestamp >> 8); - _nonce[7] = (byte)(_timestamp >> 0); + _header[4] = (byte)(_timestamp >> 24); + _header[5] = (byte)(_timestamp >> 16); + _header[6] = (byte)(_timestamp >> 8); + _header[7] = (byte)(_timestamp >> 0); } + Buffer.BlockCopy(_header, 0, _buffer, 0, 12); //Copy RTP header from to the buffer + Buffer.BlockCopy(buffer, offset, _buffer, 12, count); - count = SecretBox.Encrypt(buffer, offset, count, _buffer, 12, _nonce, _secretKey); - Buffer.BlockCopy(_nonce, 0, _buffer, 0, 12); //Copy the RTP header from nonce to buffer - await _target.SendAsync(_buffer, count + 12).ConfigureAwait(false); + await _next.WriteAsync(_buffer, 0, count + 12).ConfigureAwait(false); } - public override void Flush() + public override async Task FlushAsync(CancellationToken cancelToken) { - FlushAsync(CancellationToken.None).GetAwaiter().GetResult(); - } - public override async Task FlushAsync(CancellationToken cancellationToken) - { - await _target.FlushAsync(cancellationToken).ConfigureAwait(false); - } - - public override void Clear() - { - ClearAsync(CancellationToken.None).GetAwaiter().GetResult(); + await _next.FlushAsync(cancelToken).ConfigureAwait(false); } public override async Task ClearAsync(CancellationToken cancelToken) { - await _target.ClearAsync(cancelToken).ConfigureAwait(false); + await _next.ClearAsync(cancelToken).ConfigureAwait(false); } - - public override long Length { get { throw new NotSupportedException(); } } - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } - public override void SetLength(long value) { throw new NotSupportedException(); } - public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } } } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs new file mode 100644 index 000000000..fa2c0ec0a --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Concurrent; + +namespace Discord.Audio.Streams +{ + /// Decrypts an RTP frame using libsodium + public class SodiumDecryptStream : AudioInStream + { + private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer + private readonly AudioClient _audioClient; + private readonly byte[] _buffer, _nonce, _secretKey; + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + + internal SodiumDecryptStream(AudioClient audioClient, byte[] secretKey, int bufferSize = 4000) + { + _audioClient = audioClient; + _secretKey = secretKey; + _buffer = new byte[bufferSize]; + _queuedData = new BlockingCollection(100); + _nonce = new byte[24]; + } + + public override int Read(byte[] buffer, int offset, int count) + { + var queuedData = _queuedData.Take(); + Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); + return queuedData.Length; + } + public override void Write(byte[] buffer, int offset, int count) + { + Buffer.BlockCopy(buffer, 0, _nonce, 0, 12); //Copy RTP header to nonce + count = SecretBox.Decrypt(buffer, offset, count, _buffer, 0, _nonce, _secretKey); + + var newBuffer = new byte[count]; + Buffer.BlockCopy(_buffer, 0, newBuffer, 0, count); + _queuedData.Add(newBuffer); + } + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs new file mode 100644 index 000000000..ef2fc1b27 --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio.Streams +{ + /// Encrypts an RTP frame using libsodium + public class SodiumEncryptStream : AudioOutStream + { + private readonly AudioOutStream _next; + private readonly byte[] _nonce, _secretKey; + + //protected readonly byte[] _buffer; + + internal SodiumEncryptStream(AudioOutStream next, byte[] secretKey/*, int bufferSize = 4000*/) + { + _next = next; + _secretKey = secretKey; + //_buffer = new byte[bufferSize]; //TODO: Can Sodium do an in-place encrypt? + _nonce = new byte[24]; + } + + public override void Write(byte[] buffer, int offset, int count) + { + WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + Buffer.BlockCopy(buffer, offset, _nonce, 0, 12); //Copy nonce from RTP header + count = SecretBox.Encrypt(buffer, offset + 12, count - 12, buffer, 12, _nonce, _secretKey); + await _next.WriteAsync(buffer, 0, count + 12, cancellationToken).ConfigureAwait(false); + } + + public override async Task FlushAsync(CancellationToken cancelToken) + { + await _next.FlushAsync(cancelToken).ConfigureAwait(false); + } + public override async Task ClearAsync(CancellationToken cancelToken) + { + await _next.ClearAsync(cancelToken).ConfigureAwait(false); + } + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs deleted file mode 100644 index b27c5c8b3..000000000 --- a/src/Discord.Net.WebSocket/Audio/Targets/BufferedAudioTarget.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Threading; -using System.Threading.Tasks; - -namespace Discord.Audio -{ - internal class BufferedAudioTarget : IAudioTarget, IDisposable - { - private struct Frame - { - public Frame(byte[] buffer, int bytes) - { - Buffer = buffer; - Bytes = bytes; - } - - public readonly byte[] Buffer; - public readonly int Bytes; - } - - private static readonly byte[] _silenceFrame = new byte[] { 0xF8, 0xFF, 0xFE }; - - private Task _task; - private DiscordVoiceAPIClient _client; - private CancellationTokenSource _cancelTokenSource; - private CancellationToken _cancelToken; - private ConcurrentQueue _queuedFrames; - private ConcurrentQueue _bufferPool; - private SemaphoreSlim _queueLock; - private int _ticksPerFrame; - - internal BufferedAudioTarget(DiscordVoiceAPIClient client, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken) - { - _client = client; - _ticksPerFrame = samplesPerFrame / 48; - int queueLength = (bufferMillis + (_ticksPerFrame - 1)) / _ticksPerFrame; //Round up - - _cancelTokenSource = new CancellationTokenSource(); - _cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, cancelToken).Token; - _queuedFrames = new ConcurrentQueue(); - _bufferPool = new ConcurrentQueue(); - for (int i = 0; i < queueLength; i++) - _bufferPool.Enqueue(new byte[1275]); - _queueLock = new SemaphoreSlim(queueLength, queueLength); - - _task = Run(); - } - - private Task Run() - { - return Task.Run(async () => - { - try - { - long nextTick = Environment.TickCount; - while (!_cancelToken.IsCancellationRequested) - { - long tick = Environment.TickCount; - long dist = nextTick - tick; - if (dist <= 0) - { - Frame frame; - if (_queuedFrames.TryDequeue(out frame)) - { - await _client.SendAsync(frame.Buffer, frame.Bytes).ConfigureAwait(false); - _bufferPool.Enqueue(frame.Buffer); - _queueLock.Release(); - } - else - await _client.SendAsync(_silenceFrame, _silenceFrame.Length).ConfigureAwait(false); - nextTick += _ticksPerFrame; - } - else if (dist > 1) - await Task.Delay((int)dist).ConfigureAwait(false); - } - } - catch (OperationCanceledException) { } - }); - } - - public async Task SendAsync(byte[] data, int count) - { - await _queueLock.WaitAsync(-1, _cancelToken).ConfigureAwait(false); - byte[] buffer; - _bufferPool.TryDequeue(out buffer); - Buffer.BlockCopy(data, 0, buffer, 0, count); - _queuedFrames.Enqueue(new Frame(buffer, count)); - } - - public async Task FlushAsync(CancellationToken cancelToken) - { - while (true) - { - cancelToken.ThrowIfCancellationRequested(); - if (_queuedFrames.Count == 0) - return; - await Task.Delay(250, cancelToken).ConfigureAwait(false); - } - } - public Task ClearAsync(CancellationToken cancelToken) - { - Frame ignored; - do - cancelToken.ThrowIfCancellationRequested(); - while (_queuedFrames.TryDequeue(out ignored)); - return Task.Delay(0); - } - protected void Dispose(bool disposing) - { - if (disposing) - _cancelTokenSource.Cancel(); - } - public void Dispose() - { - Dispose(true); - } - } -} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs deleted file mode 100644 index 2440fc0a8..000000000 --- a/src/Discord.Net.WebSocket/Audio/Targets/DirectAudioTarget.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace Discord.Audio -{ - internal class DirectAudioTarget : IAudioTarget - { - private readonly DiscordVoiceAPIClient _client; - public DirectAudioTarget(DiscordVoiceAPIClient client) - { - _client = client; - } - - public Task SendAsync(byte[] buffer, int count) - => _client.SendAsync(buffer, count); - - public Task FlushAsync(CancellationToken cancelToken) - => Task.Delay(0); - public Task ClearAsync(CancellationToken cancelToken) - => Task.Delay(0); - } -} diff --git a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs b/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs deleted file mode 100644 index 1aa0d4ade..000000000 --- a/src/Discord.Net.WebSocket/Audio/Targets/IAudioTarget.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace Discord.Audio -{ - internal interface IAudioTarget - { - Task SendAsync(byte[] buffer, int count); - Task FlushAsync(CancellationToken cancelToken); - Task ClearAsync(CancellationToken cancelToken); - } -} diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index 27d2a8003..fa619b58c 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -64,7 +64,7 @@ namespace Discord.Audio }; WebSocketClient = webSocketProvider(); - //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+) + //_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); //(Causes issues in .Net 4.6+) WebSocketClient.BinaryMessage += async (data, index, count) => { using (var compressed = new MemoryStream(data, index + 2, count - 2)) @@ -117,9 +117,9 @@ namespace Discord.Audio await WebSocketClient.SendAsync(bytes, 0, bytes.Length, true).ConfigureAwait(false); await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false); } - public async Task SendAsync(byte[] data, int bytes) + public async Task SendAsync(byte[] data, int offset, int bytes) { - await _udp.SendAsync(data, 0, bytes).ConfigureAwait(false); + await _udp.SendAsync(data, offset, bytes).ConfigureAwait(false); await _sentDataEvent.InvokeAsync(bytes).ConfigureAwait(false); } @@ -224,7 +224,7 @@ namespace Discord.Audio packet[1] = (byte)(ssrc >> 16); packet[2] = (byte)(ssrc >> 8); packet[3] = (byte)(ssrc >> 0); - await SendAsync(packet, 70).ConfigureAwait(false); + await SendAsync(packet, 0, 70).ConfigureAwait(false); await _sentDiscoveryEvent.InvokeAsync().ConfigureAwait(false); } From 4c2221dacbce3ecea3a6ff5021a3d8f1f1f6fdbf Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 26 Feb 2017 13:43:11 -0400 Subject: [PATCH 259/263] More audio cleanup, finished receive streams --- src/Discord.Net.Core/Audio/AudioInStream.cs | 11 +++ src/Discord.Net.Core/Audio/RTPFrame.cs | 16 ++++ .../Audio/Opus/OpusDecoder.cs | 2 + .../Audio/Streams/BufferedWriteStream.cs | 6 +- .../Audio/Streams/InputStream.cs | 73 +++++++++++++++++++ .../Audio/Streams/OpusDecodeStream.cs | 31 ++++---- .../Audio/Streams/OpusEncodeStream.cs | 6 +- .../Audio/Streams/RTPReadStream.cs | 58 ++++++--------- .../Audio/Streams/RTPWriteStream.cs | 7 +- .../Audio/Streams/SodiumDecryptStream.cs | 34 +++++---- .../Audio/Streams/SodiumEncryptStream.cs | 12 +-- 11 files changed, 173 insertions(+), 83 deletions(-) create mode 100644 src/Discord.Net.Core/Audio/RTPFrame.cs create mode 100644 src/Discord.Net.WebSocket/Audio/Streams/InputStream.cs diff --git a/src/Discord.Net.Core/Audio/AudioInStream.cs b/src/Discord.Net.Core/Audio/AudioInStream.cs index 4023f9c86..a6b5c5e6b 100644 --- a/src/Discord.Net.Core/Audio/AudioInStream.cs +++ b/src/Discord.Net.Core/Audio/AudioInStream.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio { @@ -10,6 +11,16 @@ namespace Discord.Audio public override bool CanSeek => false; public override bool CanWrite => true; + public abstract Task ReadFrameAsync(CancellationToken cancelToken); + + public RTPFrame? ReadFrame() + { + return ReadFrameAsync(CancellationToken.None).GetAwaiter().GetResult(); + } + public override int Read(byte[] buffer, int offset, int count) + { + return ReadAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); + } public override void Write(byte[] buffer, int offset, int count) { WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); diff --git a/src/Discord.Net.Core/Audio/RTPFrame.cs b/src/Discord.Net.Core/Audio/RTPFrame.cs new file mode 100644 index 000000000..5005870f4 --- /dev/null +++ b/src/Discord.Net.Core/Audio/RTPFrame.cs @@ -0,0 +1,16 @@ +namespace Discord.Audio +{ + public struct RTPFrame + { + public readonly ushort Sequence; + public readonly uint Timestamp; + public readonly byte[] Payload; + + public RTPFrame(ushort sequence, uint timestamp, byte[] payload) + { + Sequence = sequence; + Timestamp = timestamp; + Payload = payload; + } + } +} \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs index ea9376f82..b2ecf5987 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusDecoder.cs @@ -11,6 +11,8 @@ namespace Discord.Audio private static extern void DestroyDecoder(IntPtr decoder); [DllImport("opus", EntryPoint = "opus_decode", CallingConvention = CallingConvention.Cdecl)] private static extern int Decode(IntPtr st, byte* data, int len, byte* pcm, int max_frame_size, int decode_fec); + [DllImport("opus", EntryPoint = "opus_decoder_ctl", CallingConvention = CallingConvention.Cdecl)] + private static extern int DecoderCtl(IntPtr st, OpusCtl request, int value); public OpusDecoder(int samplingRate, int channels) : base(samplingRate, channels) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs index f12417d4e..0eccd1a4c 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs @@ -34,6 +34,8 @@ namespace Discord.Audio.Streams private readonly int _ticksPerFrame, _queueLength; private bool _isPreloaded; + public BufferedWriteStream(AudioOutStream next, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken, int maxFrameSize = 1500) + : this(next, samplesPerFrame, bufferMillis, cancelToken, null, maxFrameSize) { } internal BufferedWriteStream(AudioOutStream next, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken, Logger logger, int maxFrameSize = 1500) { //maxFrameSize = 1275 was too limiting at 128*1024 @@ -55,7 +57,9 @@ namespace Discord.Audio.Streams private Task Run() { +#if DEBUG uint num = 0; +#endif return Task.Run(async () => { try @@ -92,7 +96,7 @@ namespace Discord.Audio.Streams } } else - await Task.Delay((int)(dist - (limit - 1))).ConfigureAwait(false); + await Task.Delay((int)(dist - (limit - 1))/*, _cancelToken*/).ConfigureAwait(false); } } catch (OperationCanceledException) { } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/InputStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/InputStream.cs new file mode 100644 index 000000000..d46db128b --- /dev/null +++ b/src/Discord.Net.WebSocket/Audio/Streams/InputStream.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; + +namespace Discord.Audio.Streams +{ + /// Reads the payload from an RTP frame + public class InputStream : AudioInStream + { + private ConcurrentQueue _frames; + private ushort _nextSeq; + private uint _nextTimestamp; + private bool _hasHeader; + + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => true; + + public InputStream(byte[] secretKey) + { + _frames = new ConcurrentQueue(); + } + + public override Task ReadFrameAsync(CancellationToken cancelToken) + { + if (_frames.TryDequeue(out var frame)) + return Task.FromResult(frame); + return Task.FromResult(null); + } + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) + { + cancelToken.ThrowIfCancellationRequested(); + + if (_frames.TryDequeue(out var frame)) + { + if (count < frame.Payload.Length) + throw new InvalidOperationException("Buffer is too small."); + Buffer.BlockCopy(frame.Payload, 0, buffer, offset, frame.Payload.Length); + return Task.FromResult(frame.Payload.Length); + } + return Task.FromResult(0); + } + + public void WriteHeader(ushort seq, uint timestamp) + { + if (_hasHeader) + throw new InvalidOperationException("Header received with no payload"); + _hasHeader = true; + _nextSeq = seq; + _nextTimestamp = timestamp; + } + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) + { + cancelToken.ThrowIfCancellationRequested(); + + if (_frames.Count > 1000) + return Task.Delay(0); //Buffer overloaded + if (_hasHeader) + throw new InvalidOperationException("Received payload with an RTP header"); + byte[] payload = new byte[count]; + Buffer.BlockCopy(buffer, offset, payload, 0, count); + + _frames.Enqueue(new RTPFrame( + sequence: _nextSeq, + timestamp: _nextTimestamp, + payload: payload + )); + _hasHeader = false; + return Task.Delay(0); + } + } +} diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs index c700a7f15..9df553bfe 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusDecodeStream.cs @@ -1,34 +1,35 @@ -using System; -using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio.Streams { /// Converts Opus to PCM - public class OpusDecodeStream : AudioInStream + public class OpusDecodeStream : AudioOutStream { - private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer + private readonly AudioOutStream _next; private readonly byte[] _buffer; private readonly OpusDecoder _decoder; - internal OpusDecodeStream(AudioClient audioClient, int samplingRate, int channels = OpusConverter.MaxChannels, int bufferSize = 4000) + public OpusDecodeStream(AudioOutStream next, int samplingRate, int channels = OpusConverter.MaxChannels, int bufferSize = 4000) { + _next = next; _buffer = new byte[bufferSize]; _decoder = new OpusDecoder(samplingRate, channels); - _queuedData = new BlockingCollection(100); } - public override int Read(byte[] buffer, int offset, int count) + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - var queuedData = _queuedData.Take(); - Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); - return queuedData.Length; + count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0); + await _next.WriteAsync(_buffer, 0, count, cancellationToken).ConfigureAwait(false); } - public override void Write(byte[] buffer, int offset, int count) + + public override async Task FlushAsync(CancellationToken cancelToken) { - count = _decoder.DecodeFrame(buffer, offset, count, _buffer, 0); - var newBuffer = new byte[count]; - Buffer.BlockCopy(_buffer, 0, newBuffer, 0, count); - _queuedData.Add(newBuffer); + await _next.FlushAsync(cancelToken).ConfigureAwait(false); + } + public override async Task ClearAsync(CancellationToken cancelToken) + { + await _next.ClearAsync(cancelToken).ConfigureAwait(false); } protected override void Dispose(bool disposing) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs index 01747fc05..ada8311fe 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/OpusEncodeStream.cs @@ -17,7 +17,7 @@ namespace Discord.Audio.Streams private byte[] _partialFrameBuffer; private int _partialFramePos; - internal OpusEncodeStream(AudioOutStream next, int channels, int samplesPerFrame, int bitrate, AudioApplication application, int bufferSize = 4000) + public OpusEncodeStream(AudioOutStream next, int channels, int samplesPerFrame, int bitrate, AudioApplication application, int bufferSize = 4000) { _next = next; _encoder = new OpusEncoder(SampleRate, channels, bitrate, application); @@ -26,10 +26,6 @@ namespace Discord.Audio.Streams _partialFrameBuffer = new byte[_frameSize]; } - public override void Write(byte[] buffer, int offset, int count) - { - WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { //Assume threadsafe diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs index 0cc7a1529..9a57612bf 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPReadStream.cs @@ -1,59 +1,49 @@ using System; -using System.Collections.Concurrent; using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio.Streams { /// Reads the payload from an RTP frame - public class RTPReadStream : AudioInStream + public class RTPReadStream : AudioOutStream { - private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer - //private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer - private readonly AudioClient _audioClient; + private readonly InputStream _queue; + private readonly AudioOutStream _next; private readonly byte[] _buffer, _nonce, _secretKey; public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => true; - internal RTPReadStream(AudioClient audioClient, byte[] secretKey, int bufferSize = 4000) + public RTPReadStream(InputStream queue, byte[] secretKey, int bufferSize = 4000) + : this(queue, null, secretKey, bufferSize) { } + public RTPReadStream(InputStream queue, AudioOutStream next, byte[] secretKey, int bufferSize = 4000) { - _audioClient = audioClient; + _queue = queue; + _next = next; _secretKey = secretKey; _buffer = new byte[bufferSize]; - _queuedData = new BlockingCollection(100); _nonce = new byte[24]; } - /*public RTPFrame ReadFrame() - { - var queuedData = _queuedData.Take(); - Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); - return queuedData.Length; - }*/ - public override int Read(byte[] buffer, int offset, int count) + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { - var queuedData = _queuedData.Take(); - Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); - return queuedData.Length; - } - public override void Write(byte[] buffer, int offset, int count) - { - var newBuffer = new byte[count]; - Buffer.BlockCopy(buffer, 0, newBuffer, 0, count); - _queuedData.Add(newBuffer); - } + cancelToken.ThrowIfCancellationRequested(); - public override void Flush() { throw new NotSupportedException(); } + var payload = new byte[count - 12]; + Buffer.BlockCopy(buffer, offset + 12, payload, 0, count - 12); - public override long Length { get { throw new NotSupportedException(); } } - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } + ushort seq = (ushort)((buffer[offset + 3] << 8) | + (buffer[offset + 2] << 0)); + + uint timestamp = (uint)((buffer[offset + 4] << 24) | + (buffer[offset + 5] << 16) | + (buffer[offset + 6] << 16) | + (buffer[offset + 7] << 0)); - public override void SetLength(long value) { throw new NotSupportedException(); } - public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + _queue.WriteHeader(seq, timestamp); + await (_next ?? _queue as Stream).WriteAsync(buffer, offset, count, cancelToken).ConfigureAwait(false); + } } } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs index 5b8877f8e..836cb4852 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/RTPWriteStream.cs @@ -14,7 +14,7 @@ namespace Discord.Audio.Streams protected readonly byte[] _buffer; - internal RTPWriteStream(AudioOutStream next, int samplesPerFrame, uint ssrc, int bufferSize = 4000) + public RTPWriteStream(AudioOutStream next, int samplesPerFrame, uint ssrc, int bufferSize = 4000) { _next = next; _samplesPerFrame = samplesPerFrame; @@ -29,13 +29,10 @@ namespace Discord.Audio.Streams _header[11] = (byte)(_ssrc >> 0); } - public override void Write(byte[] buffer, int offset, int count) - { - WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - } public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + unchecked { if (_header[3]++ == byte.MaxValue) diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs index fa2c0ec0a..f1421d28b 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumDecryptStream.cs @@ -1,42 +1,46 @@ using System; -using System.Collections.Concurrent; +using System.Threading; +using System.Threading.Tasks; namespace Discord.Audio.Streams { /// Decrypts an RTP frame using libsodium - public class SodiumDecryptStream : AudioInStream + public class SodiumDecryptStream : AudioOutStream { - private readonly BlockingCollection _queuedData; //TODO: Replace with max-length ring buffer - private readonly AudioClient _audioClient; + private readonly AudioOutStream _next; private readonly byte[] _buffer, _nonce, _secretKey; public override bool CanRead => true; public override bool CanSeek => false; public override bool CanWrite => true; - internal SodiumDecryptStream(AudioClient audioClient, byte[] secretKey, int bufferSize = 4000) + public SodiumDecryptStream(AudioOutStream next, byte[] secretKey, int bufferSize = 4000) { - _audioClient = audioClient; + _next = next; _secretKey = secretKey; _buffer = new byte[bufferSize]; - _queuedData = new BlockingCollection(100); _nonce = new byte[24]; } - public override int Read(byte[] buffer, int offset, int count) - { - var queuedData = _queuedData.Take(); - Buffer.BlockCopy(queuedData, 0, buffer, offset, Math.Min(queuedData.Length, count)); - return queuedData.Length; - } - public override void Write(byte[] buffer, int offset, int count) + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { + cancelToken.ThrowIfCancellationRequested(); + Buffer.BlockCopy(buffer, 0, _nonce, 0, 12); //Copy RTP header to nonce count = SecretBox.Decrypt(buffer, offset, count, _buffer, 0, _nonce, _secretKey); var newBuffer = new byte[count]; Buffer.BlockCopy(_buffer, 0, newBuffer, 0, count); - _queuedData.Add(newBuffer); + await _next.WriteAsync(buffer, 0, count + 12, cancelToken).ConfigureAwait(false); + } + + public override async Task FlushAsync(CancellationToken cancelToken) + { + await _next.FlushAsync(cancelToken).ConfigureAwait(false); + } + public override async Task ClearAsync(CancellationToken cancelToken) + { + await _next.ClearAsync(cancelToken).ConfigureAwait(false); } } } diff --git a/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs index ef2fc1b27..90bc35e9d 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/SodiumEncryptStream.cs @@ -12,7 +12,7 @@ namespace Discord.Audio.Streams //protected readonly byte[] _buffer; - internal SodiumEncryptStream(AudioOutStream next, byte[] secretKey/*, int bufferSize = 4000*/) + public SodiumEncryptStream(AudioOutStream next, byte[] secretKey/*, int bufferSize = 4000*/) { _next = next; _secretKey = secretKey; @@ -20,17 +20,13 @@ namespace Discord.Audio.Streams _nonce = new byte[24]; } - public override void Write(byte[] buffer, int offset, int count) + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancelToken) { - WriteAsync(buffer, offset, count, CancellationToken.None).GetAwaiter().GetResult(); - } - public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + cancelToken.ThrowIfCancellationRequested(); Buffer.BlockCopy(buffer, offset, _nonce, 0, 12); //Copy nonce from RTP header count = SecretBox.Encrypt(buffer, offset + 12, count - 12, buffer, 12, _nonce, _secretKey); - await _next.WriteAsync(buffer, 0, count + 12, cancellationToken).ConfigureAwait(false); + await _next.WriteAsync(buffer, 0, count + 12, cancelToken).ConfigureAwait(false); } public override async Task FlushAsync(CancellationToken cancelToken) From 66b7e0e7f03ed40cbd42f432edba03601bb786da Mon Sep 17 00:00:00 2001 From: Christopher F Date: Wed, 1 Mar 2017 17:16:06 -0500 Subject: [PATCH 260/263] Rename precondition to 'YoungerThanTwoWeeks' --- src/Discord.Net.Core/Utils/Preconditions.cs | 2 +- src/Discord.Net.Rest/DiscordRestApiClient.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Core/Utils/Preconditions.cs b/src/Discord.Net.Core/Utils/Preconditions.cs index 5c5cba7e6..65af6e49b 100644 --- a/src/Discord.Net.Core/Utils/Preconditions.cs +++ b/src/Discord.Net.Core/Utils/Preconditions.cs @@ -183,7 +183,7 @@ namespace Discord } // Bulk Delete - public static void NoMessageOlderThanTwoWeeks(ulong[] collection, string name) + public static void YoungerThanTwoWeeks(ulong[] collection, string name) { var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.Now.Subtract(TimeSpan.FromMilliseconds(1209540000))); for (var i = 0; i < collection.Length; i++) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 0700b69ea..9e9ac4611 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -484,7 +484,7 @@ namespace Discord.API Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args.MessageIds, nameof(args.MessageIds)); Preconditions.AtMost(args.MessageIds.Length, 100, nameof(args.MessageIds.Length)); - Preconditions.NoMessageOlderThanTwoWeeks(args.MessageIds, nameof(args.MessageIds)); + Preconditions.YoungerThanTwoWeeks(args.MessageIds, nameof(args.MessageIds)); options = RequestOptions.CreateOrClone(options); switch (args.MessageIds.Length) From 1cd67c246705bea97d3c5d63bbf8a9f9cf0d728c Mon Sep 17 00:00:00 2001 From: RogueException Date: Sun, 26 Feb 2017 17:27:32 -0400 Subject: [PATCH 261/263] Allow EmbedFieldBuilder to take object values --- src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs index d798ec60d..8890df683 100644 --- a/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Rest/Entities/Messages/EmbedBuilder.cs @@ -123,7 +123,7 @@ namespace Discord private EmbedField _field; public string Name { get { return _field.Name; } set { _field.Name = value; } } - public string Value { get { return _field.Value; } set { _field.Value = value; } } + public object Value { get { return _field.Value; } set { _field.Value = value.ToString(); } } public bool IsInline { get { return _field.Inline; } set { _field.Inline = value; } } public EmbedFieldBuilder() @@ -136,7 +136,7 @@ namespace Discord Name = name; return this; } - public EmbedFieldBuilder WithValue(string value) + public EmbedFieldBuilder WithValue(object value) { Value = value; return this; From 8c75e0d581bf1c1bb0030996780f0661fddd7ec6 Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 2 Mar 2017 06:12:14 -0400 Subject: [PATCH 262/263] Fixed a couple audio bugs --- .../Audio/AudioClient.cs | 8 +++---- .../Audio/Opus/OpusEncoder.cs | 9 +++++--- .../Audio/Streams/BufferedWriteStream.cs | 22 ++++++++++--------- .../DiscordSocketClient.cs | 2 +- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 7645604df..e2586d0f3 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -253,7 +253,7 @@ namespace Discord.Audio private async Task RunHeartbeatAsync(int intervalMillis, CancellationToken cancelToken) { - //Clean this up when Discord's session patch is live + //TODO: Clean this up when Discord's session patch is live try { await _audioLogger.DebugAsync("Heartbeat Started").ConfigureAwait(false); @@ -261,17 +261,15 @@ namespace Discord.Audio { var now = Environment.TickCount; - //Did server respond to our last heartbeat, or are we still receiving messages (long load?) + //Did server respond to our last heartbeat? if (_heartbeatTimes.Count != 0 && (now - _lastMessageTime) > intervalMillis && ConnectionState == ConnectionState.Connected) { _connection.Error(new Exception("Server missed last heartbeat")); return; } - _heartbeatTimes.Enqueue(now); - - await Task.Delay(intervalMillis, cancelToken).ConfigureAwait(false); + _heartbeatTimes.Enqueue(now); try { await ApiClient.SendHeartbeatAsync().ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs index ca87c5fc6..1f0b35d77 100644 --- a/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs +++ b/src/Discord.Net.WebSocket/Audio/Opus/OpusEncoder.cs @@ -68,9 +68,12 @@ namespace Discord.Audio if (result < 0) throw new Exception($"Opus Error: {(OpusError)result}"); - /*result = EncoderCtl(_ptr, OpusCtl.SetBandwidth, 1105); - if (result < 0) - throw new Exception($"Opus Error: {(OpusError)result}");*/ + /*if (application == AudioApplication.Music) + { + result = EncoderCtl(_ptr, OpusCtl.SetBandwidth, 1105); + if (result < 0) + throw new Exception($"Opus Error: {(OpusError)result}"); + }*/ } /// Produces Opus encoded audio from PCM samples. diff --git a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs index 0eccd1a4c..dcd053cc1 100644 --- a/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs +++ b/src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs @@ -38,7 +38,7 @@ namespace Discord.Audio.Streams : this(next, samplesPerFrame, bufferMillis, cancelToken, null, maxFrameSize) { } internal BufferedWriteStream(AudioOutStream next, int samplesPerFrame, int bufferMillis, CancellationToken cancelToken, Logger logger, int maxFrameSize = 1500) { - //maxFrameSize = 1275 was too limiting at 128*1024 + //maxFrameSize = 1275 was too limiting at 128kbps,2ch,60ms _next = next; _ticksPerFrame = samplesPerFrame / 48; _logger = logger; @@ -57,11 +57,11 @@ namespace Discord.Audio.Streams private Task Run() { -#if DEBUG - uint num = 0; -#endif return Task.Run(async () => { +#if DEBUG + uint num = 0; +#endif try { while (!_isPreloaded && !_cancelToken.IsCancellationRequested) @@ -70,10 +70,9 @@ namespace Discord.Audio.Streams long nextTick = Environment.TickCount; while (!_cancelToken.IsCancellationRequested) { - const int limit = 1; long tick = Environment.TickCount; long dist = nextTick - tick; - if (dist <= limit) + if (dist <= 0) { Frame frame; if (_queuedFrames.TryDequeue(out frame)) @@ -86,17 +85,20 @@ namespace Discord.Audio.Streams var _ = _logger.DebugAsync($"{num++}: Sent {frame.Bytes} bytes ({_queuedFrames.Count} frames buffered)"); #endif } - else if (dist == 0) + else { - await _next.WriteAsync(_silenceFrame, 0, _silenceFrame.Length).ConfigureAwait(false); - nextTick += _ticksPerFrame; + while ((nextTick - tick) <= 0) + { + await _next.WriteAsync(_silenceFrame, 0, _silenceFrame.Length).ConfigureAwait(false); + nextTick += _ticksPerFrame; + } #if DEBUG var _ = _logger.DebugAsync($"{num++}: Buffer underrun"); #endif } } else - await Task.Delay((int)(dist - (limit - 1))/*, _cancelToken*/).ConfigureAwait(false); + await Task.Delay((int)(dist)/*, _cancelToken*/).ConfigureAwait(false); } } catch (OperationCanceledException) { } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 52d79a04b..66c25e5f6 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1556,8 +1556,8 @@ namespace Discord.WebSocket return; } } - _heartbeatTimes.Enqueue(now); + _heartbeatTimes.Enqueue(now); try { await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); From 1b0e47be42a5dbff4bf32bb2842348de5e075ced Mon Sep 17 00:00:00 2001 From: RogueException Date: Thu, 2 Mar 2017 06:55:09 -0400 Subject: [PATCH 263/263] Fixed SocketRole.CompareTo --- .../{Extensions/RoleExtensions.cs => Utils/RoleUtils.cs} | 4 ++-- src/Discord.Net.Rest/Entities/Roles/RestRole.cs | 2 +- src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) rename src/Discord.Net.Core/{Extensions/RoleExtensions.cs => Utils/RoleUtils.cs} (79%) diff --git a/src/Discord.Net.Core/Extensions/RoleExtensions.cs b/src/Discord.Net.Core/Utils/RoleUtils.cs similarity index 79% rename from src/Discord.Net.Core/Extensions/RoleExtensions.cs rename to src/Discord.Net.Core/Utils/RoleUtils.cs index bd6856501..444afe62e 100644 --- a/src/Discord.Net.Core/Extensions/RoleExtensions.cs +++ b/src/Discord.Net.Core/Utils/RoleUtils.cs @@ -1,8 +1,8 @@ namespace Discord { - internal static class RoleExtensions + internal static class RoleUtils { - internal static int Compare(this IRole left, IRole right) + public static int Compare(IRole left, IRole right) { if (left == null) return -1; diff --git a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs index 7e9f83855..dfdbb150d 100644 --- a/src/Discord.Net.Rest/Entities/Roles/RestRole.cs +++ b/src/Discord.Net.Rest/Entities/Roles/RestRole.cs @@ -51,7 +51,7 @@ namespace Discord.Rest public Task DeleteAsync(RequestOptions options = null) => RoleHelper.DeleteAsync(this, Discord, options); - public int CompareTo(IRole role) => this.Compare(role); + public int CompareTo(IRole role) => RoleUtils.Compare(this, role); public override string ToString() => Name; private string DebuggerDisplay => $"{Name} ({Id})"; diff --git a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs index ad4b3afff..61fd4310f 100644 --- a/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs +++ b/src/Discord.Net.WebSocket/Entities/Roles/SocketRole.cs @@ -54,8 +54,9 @@ namespace Discord.WebSocket private string DebuggerDisplay => $"{Name} ({Id})"; internal SocketRole Clone() => MemberwiseClone() as SocketRole; + public int CompareTo(IRole role) => RoleUtils.Compare(this, role); + //IRole IGuild IRole.Guild => Guild; - public int CompareTo(IRole role) => this.CompareTo(role); } }