From c618cb3ccd6e9ebf2d5b85e872379272487f6395 Mon Sep 17 00:00:00 2001 From: HelpfulStranger999 Date: Sat, 21 Apr 2018 13:41:16 -0500 Subject: [PATCH 01/49] Fixes RetryMode.RetryRatelimit being ignored (#1036) --- .../Net/Queue/RequestQueueBucket.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 2d96ca796..3346681b5 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -163,7 +163,7 @@ namespace Discord.Net.Queue if (!isRateLimited) throw new TimeoutException(); else - throw new RateLimitedException(request); + ThrowRetryLimit(request); } lock (_lock) @@ -181,13 +181,12 @@ namespace Discord.Net.Queue await _queue.RaiseRateLimitTriggered(Id, null).ConfigureAwait(false); } - if ((request.Options.RetryMode & RetryMode.RetryRatelimit) == 0) - throw new RateLimitedException(request); + ThrowRetryLimit(request); if (resetAt.HasValue) { if (resetAt > timeoutAt) - throw new RateLimitedException(request); + ThrowRetryLimit(request); int millis = (int)Math.Ceiling((resetAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds); #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Sleeping {millis} ms (Pre-emptive)"); @@ -198,7 +197,7 @@ namespace Discord.Net.Queue else { if ((timeoutAt.Value - DateTimeOffset.UtcNow).TotalMilliseconds < 500.0) - throw new RateLimitedException(request); + ThrowRetryLimit(request); #if DEBUG_LIMITS Debug.WriteLine($"[{id}] Sleeping 500* ms (Pre-emptive)"); #endif @@ -309,5 +308,11 @@ namespace Discord.Net.Queue } } } + + private void ThrowRetryLimit(RestRequest request) + { + if ((request.Options.RetryMode & RetryMode.RetryRatelimit) == 0) + throw new RateLimitedException(request); + } } } From 510f4745ea5f6d6fe3e802249adc79945733dcb5 Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 22 Apr 2018 02:43:00 +0800 Subject: [PATCH 02/49] Update VoiceRegion model (#1030) --- .../Entities/Guilds/IVoiceRegion.cs | 12 ++++++------ src/Discord.Net.Rest/API/Common/VoiceRegion.cs | 10 +++++----- .../Entities/Guilds/RestVoiceRegion.cs | 15 ++++++++++----- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs b/src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs index 1a76287d8..67bedbc3d 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IVoiceRegion.cs @@ -1,4 +1,4 @@ -namespace Discord +namespace Discord { public interface IVoiceRegion { @@ -10,9 +10,9 @@ bool IsVip { get; } /// Returns true if this voice region is the closest to your machine. bool IsOptimal { get; } - /// Gets an example hostname for this voice region. - string SampleHostname { get; } - /// Gets an example port for this voice region. - int SamplePort { get; } + /// Returns true if this is a deprecated voice region (avoid switching to these). + bool IsDeprecated { get; } + /// Returns true if this is a custom voice region (used for events/etc) + bool IsCustom { get; } } -} \ No newline at end of file +} diff --git a/src/Discord.Net.Rest/API/Common/VoiceRegion.cs b/src/Discord.Net.Rest/API/Common/VoiceRegion.cs index 5f31e8f64..606af07bd 100644 --- a/src/Discord.Net.Rest/API/Common/VoiceRegion.cs +++ b/src/Discord.Net.Rest/API/Common/VoiceRegion.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -13,9 +13,9 @@ namespace Discord.API public bool IsVip { get; set; } [JsonProperty("optimal")] public bool IsOptimal { get; set; } - [JsonProperty("sample_hostname")] - public string SampleHostname { get; set; } - [JsonProperty("sample_port")] - public int SamplePort { get; set; } + [JsonProperty("deprecated")] + public bool IsDeprecated { get; set; } + [JsonProperty("custom")] + public bool IsCustom { get; set; } } } diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs index 47fd2cd19..4e0c3c1ee 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestVoiceRegion.cs @@ -1,4 +1,4 @@ -using Discord.Rest; +using Discord.Rest; using System.Diagnostics; using Model = Discord.API.VoiceRegion; @@ -7,11 +7,16 @@ namespace Discord [DebuggerDisplay("{DebuggerDisplay,nq}")] public class RestVoiceRegion : RestEntity, IVoiceRegion { + /// public string Name { get; private set; } + /// public bool IsVip { get; private set; } + /// public bool IsOptimal { get; private set; } - public string SampleHostname { get; private set; } - public int SamplePort { get; private set; } + /// + public bool IsDeprecated { get; private set; } + /// + public bool IsCustom { get; private set; } internal RestVoiceRegion(BaseDiscordClient client, string id) : base(client, id) @@ -28,8 +33,8 @@ namespace Discord Name = model.Name; IsVip = model.IsVip; IsOptimal = model.IsOptimal; - SampleHostname = model.SampleHostname; - SamplePort = model.SamplePort; + IsDeprecated = model.IsDeprecated; + IsCustom = model.IsCustom; } public override string ToString() => Name; From a4d1e2bc14c09bf0db02b1291da216ba00bb5268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sun, 22 Apr 2018 03:45:26 +0900 Subject: [PATCH 03/49] Fix build fails when built on macOS and Linux (#1026) * Add EditorConfig * Add basic .NET style rules * Add naming rules * Add some more extension * Add target of the internal fields * Fix build fails when built on macOS and Linux * Use 'Condition' attributes refs: https://github.com/RogueException/Discord.Net/pull/1026/files/6f29dda78b35c4ce6296a0250d88ed223c5b363d#r181371650 --- src/Discord.Net.Core/Discord.Net.Core.csproj | 5 +++-- src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj | 5 +++-- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 5 +++-- src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj | 5 +++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index bce577ddd..7565fa178 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -4,11 +4,12 @@ Discord.Net.Core Discord The core components for the Discord.Net library. - net45;netstandard1.1;netstandard1.3 + net45;netstandard1.1;netstandard1.3 + netstandard1.1;netstandard1.3 - \ No newline at end of file + diff --git a/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj index 43d627c99..8c56a5fb4 100644 --- a/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj +++ b/src/Discord.Net.DebugTools/Discord.Net.DebugTools.csproj @@ -4,9 +4,10 @@ Discord.Net.DebugTools Discord A Discord.Net extension adding some helper classes for diagnosing issues. - net45;netstandard1.3 + net45;netstandard1.3 + netstandard1.3 - \ No newline at end of file + diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 29f79e410..0eb07a4b2 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -4,7 +4,8 @@ Discord.Net.Rest Discord.Rest A core Discord.Net library containing the REST client and models. - net45;netstandard1.1;netstandard1.3 + net45;netstandard1.1;netstandard1.3 + netstandard1.1;netstandard1.3 @@ -16,4 +17,4 @@ - \ No newline at end of file + diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index e4de43e51..251cdb18b 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -4,7 +4,8 @@ Discord.Net.WebSocket Discord.WebSocket A core Discord.Net library containing the WebSocket client and models. - net45;netstandard1.1;netstandard1.3 + net45;netstandard1.1;netstandard1.3 + netstandard1.1;netstandard1.3 true @@ -14,4 +15,4 @@ - \ No newline at end of file + From a3ce80c1dcaecc255d1650b9e227d2f30e440d0c Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 29 Apr 2018 23:08:43 +0800 Subject: [PATCH 04/49] Fix Embed.Length behavior (#1012) - This commit splits up the get statement lines for better analysis. --- src/Discord.Net.Core/Entities/Messages/Embed.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Messages/Embed.cs b/src/Discord.Net.Core/Entities/Messages/Embed.cs index 5fae7acde..dc62066eb 100644 --- a/src/Discord.Net.Core/Entities/Messages/Embed.cs +++ b/src/Discord.Net.Core/Entities/Messages/Embed.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; @@ -57,7 +57,18 @@ namespace Discord Fields = fields; } - public int Length => Title?.Length + Author?.Name?.Length + Description?.Length + Footer?.Text?.Length + Fields.Sum(f => f.Name.Length + f.Value.ToString().Length) ?? 0; + public int Length + { + get + { + int titleLength = Title?.Length ?? 0; + int authorLength = Author?.Name?.Length ?? 0; + int descriptionLength = Description?.Length ?? 0; + int footerLength = Footer?.Text?.Length ?? 0; + int fieldSum = Fields.Sum(f => f.Name?.Length + f.Value?.ToString().Length) ?? 0; + return titleLength + authorLength + descriptionLength + footerLength + fieldSum; + } + } public override string ToString() => Title; private string DebuggerDisplay => $"{Title} ({Type})"; From 6d3010065fdeddaf58701c6a59d0eb0d0749a7fd Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Sun, 29 Apr 2018 17:10:00 +0200 Subject: [PATCH 05/49] Allow setting IgnoreExtraArgs on an individual basis (#998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow setting IgnoreExtraArgs on an individual basis * Remove passing in the flag as a separate parameter * VS plz * Push the RunMode setting out to its own attribute, because fox wants consistency. Bonus: Removes the need for that godawful 'RunMode.Default'. * Revert previous commit * Fox doesn't like module-wide switches 😒 --- src/Discord.Net.Commands/Attributes/CommandAttribute.cs | 1 + src/Discord.Net.Commands/Builders/CommandBuilder.cs | 6 +++--- src/Discord.Net.Commands/Builders/ModuleBuilder.cs | 1 - src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs | 1 + src/Discord.Net.Commands/CommandParser.cs | 6 +++--- src/Discord.Net.Commands/CommandService.cs | 1 - src/Discord.Net.Commands/CommandServiceConfig.cs | 6 ------ src/Discord.Net.Commands/Info/CommandInfo.cs | 4 +++- src/Discord.Net.Commands/Info/ModuleInfo.cs | 5 ----- 9 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs index 5f8e9ceaf..a0fcf3e4a 100644 --- a/src/Discord.Net.Commands/Attributes/CommandAttribute.cs +++ b/src/Discord.Net.Commands/Attributes/CommandAttribute.cs @@ -7,6 +7,7 @@ namespace Discord.Commands { public string Text { get; } public RunMode RunMode { get; set; } = RunMode.Default; + public bool? IgnoreExtraArgs { get; set; } public CommandAttribute() { diff --git a/src/Discord.Net.Commands/Builders/CommandBuilder.cs b/src/Discord.Net.Commands/Builders/CommandBuilder.cs index b6d002c70..17a170775 100644 --- a/src/Discord.Net.Commands/Builders/CommandBuilder.cs +++ b/src/Discord.Net.Commands/Builders/CommandBuilder.cs @@ -1,8 +1,7 @@ -using System; +using System; using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; namespace Discord.Commands.Builders { @@ -22,6 +21,7 @@ namespace Discord.Commands.Builders public string PrimaryAlias { get; set; } public RunMode RunMode { get; set; } public int Priority { get; set; } + public bool IgnoreExtraArgs { get; set; } public IReadOnlyList Preconditions => _preconditions; public IReadOnlyList Parameters => _parameters; @@ -140,4 +140,4 @@ namespace Discord.Commands.Builders return new CommandInfo(this, info, service); } } -} \ No newline at end of file +} diff --git a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs index 1809c2c63..0ada5a9c2 100644 --- a/src/Discord.Net.Commands/Builders/ModuleBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleBuilder.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; namespace Discord.Commands.Builders { diff --git a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs index 1dd66cc77..cbe02aafb 100644 --- a/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs +++ b/src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs @@ -158,6 +158,7 @@ namespace Discord.Commands builder.AddAliases(command.Text); builder.RunMode = command.RunMode; builder.Name = builder.Name ?? command.Text; + builder.IgnoreExtraArgs = command.IgnoreExtraArgs ?? service._ignoreExtraArgs; break; case NameAttribute name: builder.Name = name.Text; diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index d65d99349..64daf841e 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Immutable; using System.Text; using System.Threading.Tasks; @@ -14,7 +14,7 @@ namespace Discord.Commands QuotedParameter } - public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos) + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); @@ -110,7 +110,7 @@ namespace Discord.Commands { if (curParam == null) { - if (ignoreExtraArgs) + if (command.IgnoreExtraArgs) break; else return ParseResult.FromError(CommandError.BadArgCount, "The input text has too many parameters."); diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index c880dd454..c996f7334 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Discord.Commands.Builders; using Discord.Logging; diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index 77c5b2262..f5cd14fef 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -20,11 +20,5 @@ namespace Discord.Commands /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; - - ///// Gets or sets the to use. - //public IServiceProvider ServiceProvider { get; set; } = null; - - ///// Gets or sets a factory function for the to use. - //public Func ServiceProviderFactory { get; set; } = null; } } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 6e74c8abc..1a9cf69c5 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -27,6 +27,7 @@ namespace Discord.Commands public string Remarks { get; } public int Priority { get; } public bool HasVarArgs { get; } + public bool IgnoreExtraArgs { get; } public RunMode RunMode { get; } public IReadOnlyList Aliases { get; } @@ -63,6 +64,7 @@ namespace Discord.Commands Parameters = builder.Parameters.Select(x => x.Build(this)).ToImmutableArray(); HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false; + IgnoreExtraArgs = builder.IgnoreExtraArgs; _action = builder.Callback; _commandService = service; @@ -119,7 +121,7 @@ namespace Discord.Commands return ParseResult.FromError(preconditionResult); string input = searchResult.Text.Substring(startIndex); - return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0).ConfigureAwait(false); + return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false); } public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Info/ModuleInfo.cs b/src/Discord.Net.Commands/Info/ModuleInfo.cs index 5a7f9208e..7c144599b 100644 --- a/src/Discord.Net.Commands/Info/ModuleInfo.cs +++ b/src/Discord.Net.Commands/Info/ModuleInfo.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; -using System.Reflection; using Discord.Commands.Builders; namespace Discord.Commands @@ -23,8 +22,6 @@ namespace Discord.Commands public ModuleInfo Parent { get; } public bool IsSubmodule => Parent != null; - //public TypeInfo TypeInfo { get; } - internal ModuleInfo(ModuleBuilder builder, CommandService service, IServiceProvider services, ModuleInfo parent = null) { Service = service; @@ -35,8 +32,6 @@ namespace Discord.Commands Group = builder.Group; Parent = parent; - //TypeInfo = builder.TypeInfo; - Aliases = BuildAliases(builder, service).ToImmutableArray(); Commands = builder.Commands.Select(x => x.Build(this, service)).ToImmutableArray(); Preconditions = BuildPreconditions(builder).ToImmutableArray(); From 7022149536ea1c9a92d6dce846cdf7d1df76772e Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 29 Apr 2018 23:11:06 +0800 Subject: [PATCH 06/49] Fix/Implement various invite-related behaviors (#1023) * Initial support for invite member count arg * Fix IDiscordClient#GetInviteAsync behavior - Previously, the GetInviteAsync method would return null since it couldn't be parsed as a simple RestInvite object. The object should be a RestInviteMetadata instead. * Fix methods that didn't comply with the interface * Change with_counts REST behaviour * Remove unnecessary JSON prop * Remove AcceptAsync --- src/Discord.Net.Core/Entities/Invites/IInvite.cs | 9 +++++---- src/Discord.Net.Core/IDiscordClient.cs | 2 +- src/Discord.Net.Rest/API/Common/Invite.cs | 6 +++++- src/Discord.Net.Rest/API/Rest/GetInviteParams.cs | 7 +++++++ src/Discord.Net.Rest/BaseDiscordClient.cs | 2 +- src/Discord.Net.Rest/ClientHelper.cs | 12 ++++++++---- src/Discord.Net.Rest/DiscordRestApiClient.cs | 13 ++++--------- src/Discord.Net.Rest/DiscordRestClient.cs | 8 ++++---- .../Entities/Invites/InviteHelper.cs | 7 +------ .../Entities/Invites/RestInvite.cs | 15 ++++++++++----- src/Discord.Net.WebSocket/BaseSocketClient.cs | 8 ++++---- src/Discord.Net.WebSocket/DiscordShardedClient.cs | 4 ++-- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 4 ++-- 13 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 src/Discord.Net.Rest/API/Rest/GetInviteParams.cs diff --git a/src/Discord.Net.Core/Entities/Invites/IInvite.cs b/src/Discord.Net.Core/Entities/Invites/IInvite.cs index 73555e453..0ebb65679 100644 --- a/src/Discord.Net.Core/Entities/Invites/IInvite.cs +++ b/src/Discord.Net.Core/Entities/Invites/IInvite.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Discord { @@ -22,8 +22,9 @@ namespace Discord ulong GuildId { get; } /// Gets the name of the guild this invite is linked to. string GuildName { get; } - - /// Accepts this invite and joins the target guild. This will fail on bot accounts. - Task AcceptAsync(RequestOptions options = null); + /// Gets the approximated count of online members in the guild. + int? PresenceCount { get; } + /// Gets the approximated count of total members in the guild. + int? MemberCount { get; } } } diff --git a/src/Discord.Net.Core/IDiscordClient.cs b/src/Discord.Net.Core/IDiscordClient.cs index a383c37da..083c0b512 100644 --- a/src/Discord.Net.Core/IDiscordClient.cs +++ b/src/Discord.Net.Core/IDiscordClient.cs @@ -27,7 +27,7 @@ namespace Discord Task> GetGuildsAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task CreateGuildAsync(string name, IVoiceRegion region, Stream jpegIcon = null, RequestOptions options = null); - Task GetInviteAsync(string inviteId, RequestOptions options = null); + Task GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null); Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); Task GetUserAsync(string username, string discriminator, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/API/Common/Invite.cs b/src/Discord.Net.Rest/API/Common/Invite.cs index 67a318c5a..1b35da870 100644 --- a/src/Discord.Net.Rest/API/Common/Invite.cs +++ b/src/Discord.Net.Rest/API/Common/Invite.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -11,5 +11,9 @@ namespace Discord.API public InviteGuild Guild { get; set; } [JsonProperty("channel")] public InviteChannel Channel { get; set; } + [JsonProperty("approximate_presence_count")] + public Optional PresenceCount { get; set; } + [JsonProperty("approximate_member_count")] + public Optional MemberCount { get; set; } } } diff --git a/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs b/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs new file mode 100644 index 000000000..cb8d8f7fe --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/GetInviteParams.cs @@ -0,0 +1,7 @@ +namespace Discord.API.Rest +{ + internal class GetInviteParams + { + public Optional WithCounts { get; set; } + } +} diff --git a/src/Discord.Net.Rest/BaseDiscordClient.cs b/src/Discord.Net.Rest/BaseDiscordClient.cs index f8642b96c..db8e2e691 100644 --- a/src/Discord.Net.Rest/BaseDiscordClient.cs +++ b/src/Discord.Net.Rest/BaseDiscordClient.cs @@ -148,7 +148,7 @@ namespace Discord.Rest Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => Task.FromResult>(ImmutableArray.Create()); - Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) + Task IDiscordClient.GetInviteAsync(string inviteId, bool withCount, RequestOptions options) => Task.FromResult(null); Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.Rest/ClientHelper.cs b/src/Discord.Net.Rest/ClientHelper.cs index 08305f857..a1f8ece69 100644 --- a/src/Discord.Net.Rest/ClientHelper.cs +++ b/src/Discord.Net.Rest/ClientHelper.cs @@ -50,12 +50,16 @@ namespace Discord.Rest return models.Select(x => RestConnection.Create(x)).ToImmutableArray(); } - public static async Task GetInviteAsync(BaseDiscordClient client, - string inviteId, RequestOptions options) + public static async Task GetInviteAsync(BaseDiscordClient client, + string inviteId, bool withCount, RequestOptions options) { - var model = await client.ApiClient.GetInviteAsync(inviteId, options).ConfigureAwait(false); + var args = new GetInviteParams + { + WithCounts = withCount + }; + var model = await client.ApiClient.GetInviteAsync(inviteId, args, options).ConfigureAwait(false); if (model != null) - return RestInvite.Create(client, null, null, model); + return RestInviteMetadata.Create(client, null, null, model); return null; } diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index f0c4358ad..7ec30c3aa 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -897,7 +897,7 @@ namespace Discord.API } //Guild Invites - public async Task GetInviteAsync(string inviteId, RequestOptions options = null) + public async Task GetInviteAsync(string inviteId, GetInviteParams args, RequestOptions options = null) { Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); options = RequestOptions.CreateOrClone(options); @@ -910,9 +910,11 @@ namespace Discord.API if (index >= 0) inviteId = inviteId.Substring(index + 1); + var withCounts = args.WithCounts.GetValueOrDefault(false); + try { - return await SendAsync("GET", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); + return await SendAsync("GET", () => $"invites/{inviteId}?with_counts={withCounts}", new BucketIds(), options: options).ConfigureAwait(false); } catch (HttpException ex) when (ex.HttpCode == HttpStatusCode.NotFound) { return null; } } @@ -950,13 +952,6 @@ namespace Discord.API return await SendAsync("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); } - public async Task AcceptInviteAsync(string inviteId, RequestOptions options = null) - { - Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); - options = RequestOptions.CreateOrClone(options); - - await SendAsync("POST", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); - } //Guild Members public async Task GetGuildMemberAsync(ulong guildId, ulong userId, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/DiscordRestClient.cs b/src/Discord.Net.Rest/DiscordRestClient.cs index 8850da3a5..3a596624d 100644 --- a/src/Discord.Net.Rest/DiscordRestClient.cs +++ b/src/Discord.Net.Rest/DiscordRestClient.cs @@ -56,8 +56,8 @@ namespace Discord.Rest => ClientHelper.GetConnectionsAsync(this, options); /// - public Task GetInviteAsync(string inviteId, RequestOptions options = null) - => ClientHelper.GetInviteAsync(this, inviteId, options); + public Task GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null) + => ClientHelper.GetInviteAsync(this, inviteId, withCount, options); /// public Task GetGuildAsync(ulong id, RequestOptions options = null) @@ -131,8 +131,8 @@ namespace Discord.Rest async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync(options).ConfigureAwait(false); - async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) - => await GetInviteAsync(inviteId, options).ConfigureAwait(false); + async Task IDiscordClient.GetInviteAsync(string inviteId, bool withCount, RequestOptions options) + => await GetInviteAsync(inviteId, withCount, options).ConfigureAwait(false); async Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs index 80a49e34e..ebcd93777 100644 --- a/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs +++ b/src/Discord.Net.Rest/Entities/Invites/InviteHelper.cs @@ -1,14 +1,9 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Discord.Rest { internal static class InviteHelper { - public static async Task AcceptAsync(IInvite invite, BaseDiscordClient client, - RequestOptions options) - { - await client.ApiClient.AcceptInviteAsync(invite.Code, options).ConfigureAwait(false); - } public static async Task DeleteAsync(IInvite invite, BaseDiscordClient client, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs index 900d1f0ac..18698c626 100644 --- a/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs +++ b/src/Discord.Net.Rest/Entities/Invites/RestInvite.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Diagnostics; using System.Threading.Tasks; +using Discord.API.Rest; using Model = Discord.API.Invite; namespace Discord.Rest @@ -10,6 +11,8 @@ namespace Discord.Rest { public string ChannelName { get; private set; } public string GuildName { get; private set; } + public int? PresenceCount { get; private set; } + public int? MemberCount { get; private set; } public ulong ChannelId { get; private set; } public ulong GuildId { get; private set; } internal IChannel Channel { get; private set; } @@ -36,19 +39,21 @@ namespace Discord.Rest ChannelId = model.Channel.Id; GuildName = model.Guild.Name; ChannelName = model.Channel.Name; + MemberCount = model.MemberCount.IsSpecified ? model.MemberCount.Value : null; + PresenceCount = model.PresenceCount.IsSpecified ? model.PresenceCount.Value : null; } public async Task UpdateAsync(RequestOptions options = null) { - var model = await Discord.ApiClient.GetInviteAsync(Code, options).ConfigureAwait(false); + var args = new GetInviteParams(); + if (MemberCount != null || PresenceCount != null) + args.WithCounts = true; + var model = await Discord.ApiClient.GetInviteAsync(Code, args, options).ConfigureAwait(false); Update(model); } public Task DeleteAsync(RequestOptions options = null) => InviteHelper.DeleteAsync(this, Discord, options); - public Task AcceptAsync(RequestOptions options = null) - => InviteHelper.AcceptAsync(this, Discord, options); - public override string ToString() => Url; private string DebuggerDisplay => $"{Url} ({GuildName} / {ChannelName})"; diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.cs b/src/Discord.Net.WebSocket/BaseSocketClient.cs index 923b2c23b..fb82fe14a 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.cs @@ -55,8 +55,8 @@ namespace Discord.WebSocket public Task> GetConnectionsAsync(RequestOptions options = null) => ClientHelper.GetConnectionsAsync(this, options ?? RequestOptions.Default); /// - public Task GetInviteAsync(string inviteId, RequestOptions options = null) - => ClientHelper.GetInviteAsync(this, inviteId, options ?? RequestOptions.Default); + public Task GetInviteAsync(string inviteId, bool withCount = false, RequestOptions options = null) + => ClientHelper.GetInviteAsync(this, inviteId, withCount, options ?? RequestOptions.Default); // IDiscordClient async Task IDiscordClient.GetApplicationInfoAsync(RequestOptions options) @@ -70,8 +70,8 @@ namespace Discord.WebSocket async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync(options).ConfigureAwait(false); - async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) - => await GetInviteAsync(inviteId, options).ConfigureAwait(false); + async Task IDiscordClient.GetInviteAsync(string inviteId, bool withCount, RequestOptions options) + => await GetInviteAsync(inviteId, withCount, options).ConfigureAwait(false); Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetGuild(id)); diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index ad89067df..45d76e61a 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -327,8 +327,8 @@ namespace Discord.WebSocket async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync().ConfigureAwait(false); - async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) - => await GetInviteAsync(inviteId).ConfigureAwait(false); + async Task IDiscordClient.GetInviteAsync(string inviteId, bool withCount, RequestOptions options) + => await GetInviteAsync(inviteId, withCount, options).ConfigureAwait(false); Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetGuild(id)); diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 593f796c2..0188f7e9f 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1796,8 +1796,8 @@ namespace Discord.WebSocket async Task> IDiscordClient.GetConnectionsAsync(RequestOptions options) => await GetConnectionsAsync().ConfigureAwait(false); - async Task IDiscordClient.GetInviteAsync(string inviteId, RequestOptions options) - => await GetInviteAsync(inviteId).ConfigureAwait(false); + async Task IDiscordClient.GetInviteAsync(string inviteId, bool withCount, RequestOptions options) + => await GetInviteAsync(inviteId, withCount, options).ConfigureAwait(false); Task IDiscordClient.GetGuildAsync(ulong id, CacheMode mode, RequestOptions options) => Task.FromResult(GetGuild(id)); From 9b29c004fa8216cff1bd593f6eb90936656886a8 Mon Sep 17 00:00:00 2001 From: Allan Date: Sun, 29 Apr 2018 17:11:44 +0200 Subject: [PATCH 07/49] Fixed GetReactionUsersAsync that was not using limit and afterUserId arguments. (#1017) --- src/Discord.Net.Rest/DiscordRestApiClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 7ec30c3aa..042ae9970 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -626,7 +626,7 @@ namespace Discord.API ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); var ids = new BucketIds(channelId: channelId); - Expression> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}"; + Expression> endpoint = () => $"channels/{channelId}/messages/{messageId}/reactions/{emoji}?limit={limit}&after={afterUserId}"; return await SendAsync>("GET", endpoint, ids, options: options).ConfigureAwait(false); } public async Task AckMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) From 3631886d2bf63b2a2e6175c185f51598612becf3 Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 29 Apr 2018 23:14:34 +0800 Subject: [PATCH 08/49] Resolves #1024 (#1031) - Replace 'var _' with simple '_' discard as well. --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 0188f7e9f..18413e840 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -479,14 +479,14 @@ namespace Discord.WebSocket await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); }); - var _ = _connection.CompleteAsync(); + _ = _connection.CompleteAsync(); } break; case "RESUMED": { await _gatewayLogger.DebugAsync("Received Dispatch (RESUMED)").ConfigureAwait(false); - var _ = _connection.CompleteAsync(); + _ = _connection.CompleteAsync(); //Notify the client that these guilds are available again foreach (var guild in State.Guilds) @@ -1095,8 +1095,10 @@ namespace Discord.WebSocket else if (channel is SocketGroupChannel) author = (channel as SocketGroupChannel).GetOrAddUser(data.Author.Value); else + { await UnknownChannelUserAsync(type, data.Author.Value.Id, channel.Id).ConfigureAwait(false); - return; + return; + } } var msg = SocketMessage.Create(this, State, author, channel, data); From 73dc884d47bbff897de234351b39b97979cc9f14 Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Sun, 29 Apr 2018 23:15:24 +0800 Subject: [PATCH 09/49] Improve SpotifyGame (#1039) * Initial commit * Add TrackUrl * Rename AlbumArt to AlbumArtUrl for consistency --- src/Discord.Net.Core/CDN.cs | 2 ++ .../Entities/Activities/SpotifyGame.cs | 13 ++++++++----- .../Extensions/EntityExtensions.cs | 10 +++++++--- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Discord.Net.Core/CDN.cs b/src/Discord.Net.Core/CDN.cs index 52b9a2878..6bac241aa 100644 --- a/src/Discord.Net.Core/CDN.cs +++ b/src/Discord.Net.Core/CDN.cs @@ -34,6 +34,8 @@ namespace Discord public static string GetSpotifyAlbumArtUrl(string albumArtId) => $"https://i.scdn.co/image/{albumArtId}"; + public static string GetSpotifyDirectUrl(string trackId) + => $"https://open.spotify.com/track/{trackId}"; private static string FormatToExtension(ImageFormat format, string imageId) { diff --git a/src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs b/src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs index a20384242..8b966d68b 100644 --- a/src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs +++ b/src/Discord.Net.Core/Entities/Activities/SpotifyGame.cs @@ -7,17 +7,20 @@ namespace Discord [DebuggerDisplay(@"{DebuggerDisplay,nq}")] public class SpotifyGame : Game { - public IEnumerable Artists { get; internal set; } - public string AlbumArt { get; internal set; } + public IReadOnlyCollection Artists { get; internal set; } public string AlbumTitle { get; internal set; } public string TrackTitle { get; internal set; } - public string SyncId { get; internal set; } - public string SessionId { get; internal set; } public TimeSpan? Duration { get; internal set; } + public string TrackId { get; internal set; } + public string SessionId { get; internal set; } + + public string AlbumArtUrl { get; internal set; } + public string TrackUrl { get; internal set; } + internal SpotifyGame() { } - public override string ToString() => Name; + public override string ToString() => $"{string.Join(", ", Artists)} - {TrackTitle} ({Duration})"; private string DebuggerDisplay => $"{Name} (Spotify)"; } } diff --git a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs index ff395a932..e8dc4b5f0 100644 --- a/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs +++ b/src/Discord.Net.WebSocket/Extensions/EntityExtensions.cs @@ -1,3 +1,6 @@ +using System.Collections.Immutable; +using System.Linq; + namespace Discord.WebSocket { internal static class EntityExtensions @@ -15,12 +18,13 @@ namespace Discord.WebSocket { Name = model.Name, SessionId = model.SessionId.GetValueOrDefault(), - SyncId = model.SyncId.Value, + TrackId = model.SyncId.Value, + TrackUrl = CDN.GetSpotifyDirectUrl(model.SyncId.Value), AlbumTitle = albumText, TrackTitle = model.Details.GetValueOrDefault(), - Artists = model.State.GetValueOrDefault()?.Split(';'), + Artists = model.State.GetValueOrDefault()?.Split(';').Select(x=>x?.Trim()).ToImmutableArray(), Duration = timestamps?.End - timestamps?.Start, - AlbumArt = albumArtId != null ? CDN.GetSpotifyAlbumArtUrl(albumArtId) : null, + AlbumArtUrl = albumArtId != null ? CDN.GetSpotifyAlbumArtUrl(albumArtId) : null, Type = ActivityType.Listening }; } From 217ec34ef05ef3f022e20394a62801546b2ce5d2 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 29 Apr 2018 11:16:27 -0400 Subject: [PATCH 10/49] Add sample bots to the solution (#972) * Add sample bots to the solution * Fix missing attributes, show use of preconditions --- Discord.Net.sln | 36 +++++++++- .../01_basic_ping_bot.csproj | 12 ++++ samples/01_basic_ping_bot/Program.cs | 69 +++++++++++++++++++ .../02_commands_framework.csproj | 17 +++++ .../Modules/PublicModule.cs | 63 +++++++++++++++++ samples/02_commands_framework/Program.cs | 60 ++++++++++++++++ .../Services/CommandHandlingService.cs | 49 +++++++++++++ .../Services/PictureService.cs | 20 ++++++ 8 files changed, 324 insertions(+), 2 deletions(-) create mode 100644 samples/01_basic_ping_bot/01_basic_ping_bot.csproj create mode 100644 samples/01_basic_ping_bot/Program.cs create mode 100644 samples/02_commands_framework/02_commands_framework.csproj create mode 100644 samples/02_commands_framework/Modules/PublicModule.cs create mode 100644 samples/02_commands_framework/Program.cs create mode 100644 samples/02_commands_framework/Services/CommandHandlingService.cs create mode 100644 samples/02_commands_framework/Services/PictureService.cs diff --git a/Discord.Net.sln b/Discord.Net.sln index daf902b96..9bb940d8c 100644 --- a/Discord.Net.sln +++ b/Discord.Net.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.0 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Core", "src\Discord.Net.Core\Discord.Net.Core.csproj", "{91E9E7BD-75C9-4E98-84AA-2C271922E5C2}" EndProject @@ -22,7 +22,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Tests", "test\D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Webhook", "src\Discord.Net.Webhook\Discord.Net.Webhook.csproj", "{9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Analyzers", "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj", "{BBA8E7FB-C834-40DC-822F-B112CB7F0140}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Discord.Net.Analyzers", "src\Discord.Net.Analyzers\Discord.Net.Analyzers.csproj", "{BBA8E7FB-C834-40DC-822F-B112CB7F0140}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{BB59D5B5-E7B0-4BF4-8F82-D14431B2799B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "01_basic_ping_bot", "samples\01_basic_ping_bot\01_basic_ping_bot.csproj", "{F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "02_commands_framework", "samples\02_commands_framework\02_commands_framework.csproj", "{4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -130,6 +136,30 @@ Global {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x64.Build.0 = Release|Any CPU {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.ActiveCfg = Release|Any CPU {BBA8E7FB-C834-40DC-822F-B112CB7F0140}.Release|x86.Build.0 = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|x64.ActiveCfg = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|x64.Build.0 = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|x86.ActiveCfg = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Debug|x86.Build.0 = Debug|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|Any CPU.Build.0 = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|x64.ActiveCfg = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|x64.Build.0 = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|x86.ActiveCfg = Release|Any CPU + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E}.Release|x86.Build.0 = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|x64.ActiveCfg = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|x64.Build.0 = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|x86.ActiveCfg = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Debug|x86.Build.0 = Debug|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|Any CPU.Build.0 = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|x64.ActiveCfg = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|x64.Build.0 = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|x86.ActiveCfg = Release|Any CPU + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -141,6 +171,8 @@ Global {6BDEEC08-417B-459F-9CA3-FF8BAB18CAC7} = {B0657AAE-DCC5-4FBF-8E5D-1FB578CF3012} {9AFAB80E-D2D3-4EDB-B58C-BACA78D1EA30} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} {BBA8E7FB-C834-40DC-822F-B112CB7F0140} = {CC3D4B1C-9DE0-448B-8AE7-F3F1F3EC5C3A} + {F2FF84FB-F6AD-47E5-9EE5-18206CAE136E} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} + {4E1F1F40-B1DD-40C9-A4B1-A2046A4C9C76} = {BB59D5B5-E7B0-4BF4-8F82-D14431B2799B} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D2404771-EEC8-45F2-9D71-F3373F6C1495} diff --git a/samples/01_basic_ping_bot/01_basic_ping_bot.csproj b/samples/01_basic_ping_bot/01_basic_ping_bot.csproj new file mode 100644 index 000000000..5484e3d55 --- /dev/null +++ b/samples/01_basic_ping_bot/01_basic_ping_bot.csproj @@ -0,0 +1,12 @@ + + + + Exe + netcoreapp2.0 + + + + + + + diff --git a/samples/01_basic_ping_bot/Program.cs b/samples/01_basic_ping_bot/Program.cs new file mode 100644 index 000000000..0fcc52b85 --- /dev/null +++ b/samples/01_basic_ping_bot/Program.cs @@ -0,0 +1,69 @@ +using System; +using System.Threading.Tasks; +using Discord; +using Discord.WebSocket; + +namespace _01_basic_ping_bot +{ + // This is a minimal, barebones example of using Discord.Net + // + // If writing a bot with commands, we recommend using the Discord.Net.Commands + // framework, rather than handling commands yourself, like we do in this sample. + // + // You can find samples of using the command framework: + // - Here, under the 02_commands_framework sample + // - https://github.com/foxbot/DiscordBotBase - a barebones bot template + // - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library + class Program + { + private DiscordSocketClient _client; + + // Discord.Net heavily utilizes TAP for async, so we create + // an asynchronous context from the beginning. + static void Main(string[] args) + => new Program().MainAsync().GetAwaiter().GetResult(); + + public async Task MainAsync() + { + _client = new DiscordSocketClient(); + + _client.Log += LogAsync; + _client.Ready += ReadyAsync; + _client.MessageReceived += MessageReceivedAsync; + + // Tokens should be considered secret data, and never hard-coded. + await _client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token")); + await _client.StartAsync(); + + // Block the program until it is closed. + await Task.Delay(-1); + } + + private Task LogAsync(LogMessage log) + { + Console.WriteLine(log.ToString()); + return Task.CompletedTask; + } + + // The Ready event indicates that the client has opened a + // connection and it is now safe to access the cache. + private Task ReadyAsync() + { + Console.WriteLine($"{_client.CurrentUser} is connected!"); + + return Task.CompletedTask; + } + + // This is not the recommmended way to write a bot - consider + // reading over the Commands Framework sample. + private async Task MessageReceivedAsync(SocketMessage message) + { + // The bot should never respond to itself. + if (message.Author.Id == _client.CurrentUser.Id) + return; + + if (message.Content == "!ping") + await message.Channel.SendMessageAsync("pong!"); + } + } +} diff --git a/samples/02_commands_framework/02_commands_framework.csproj b/samples/02_commands_framework/02_commands_framework.csproj new file mode 100644 index 000000000..77fdc65e1 --- /dev/null +++ b/samples/02_commands_framework/02_commands_framework.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + diff --git a/samples/02_commands_framework/Modules/PublicModule.cs b/samples/02_commands_framework/Modules/PublicModule.cs new file mode 100644 index 000000000..f30dfd73f --- /dev/null +++ b/samples/02_commands_framework/Modules/PublicModule.cs @@ -0,0 +1,63 @@ +using System.IO; +using System.Threading.Tasks; +using Discord; +using Discord.Commands; +using _02_commands_framework.Services; + +namespace _02_commands_framework.Modules +{ + // Modules must be public and inherit from an IModuleBase + public class PublicModule : ModuleBase + { + // Dependency Injection will fill this value in for us + public PictureService PictureService { get; set; } + + [Command("ping")] + [Alias("pong", "hello")] + public Task PingAsync() + => ReplyAsync("pong!"); + + [Command("cat")] + public async Task CatAsync() + { + // Get a stream containing an image of a cat + var stream = await PictureService.GetCatPictureAsync(); + // Streams must be seeked to their beginning before being uploaded! + stream.Seek(0, SeekOrigin.Begin); + await Context.Channel.SendFileAsync(stream, "cat.png"); + } + + // Get info on a user, or the user who invoked the command if one is not specified + [Command("userinfo")] + public async Task UserInfoAsync(IUser user = null) + { + user = user ?? Context.User; + + await ReplyAsync(user.ToString()); + } + + // Ban a user + [Command("ban")] + [RequireContext(ContextType.Guild)] + // make sure the user invoking the command can ban + [RequireUserPermission(GuildPermission.BanMembers)] + // make sure the bot itself can ban + [RequireBotPermission(GuildPermission.BanMembers)] + public async Task BanUserAsync(IGuildUser user, [Remainder] string reason = null) + { + await user.Guild.AddBanAsync(user, reason: reason); + await ReplyAsync("ok!"); + } + + // [Remainder] takes the rest of the command's arguments as one argument, rather than splitting every space + [Command("echo")] + public Task EchoAsync([Remainder] string text) + // Insert a ZWSP before the text to prevent triggering other bots! + => ReplyAsync('\u200B' + text); + + // 'params' will parse space-separated elements into a list + [Command("list")] + public Task ListAsync(params string[] objects) + => ReplyAsync("You listed: " + string.Join("; ", objects)); + } +} diff --git a/samples/02_commands_framework/Program.cs b/samples/02_commands_framework/Program.cs new file mode 100644 index 000000000..3fed652d3 --- /dev/null +++ b/samples/02_commands_framework/Program.cs @@ -0,0 +1,60 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Discord; +using Discord.WebSocket; +using Discord.Commands; +using _02_commands_framework.Services; + +namespace _02_commands_framework +{ + // This is a minimal example of using Discord.Net's command + // framework - by no means does it show everything the framework + // is capable of. + // + // You can find samples of using the command framework: + // - Here, under the 02_commands_framework sample + // - https://github.com/foxbot/DiscordBotBase - a barebones bot template + // - https://github.com/foxbot/patek - a more feature-filled bot, utilizing more aspects of the library + class Program + { + static void Main(string[] args) + => new Program().MainAsync().GetAwaiter().GetResult(); + + public async Task MainAsync() + { + var services = ConfigureServices(); + + var client = services.GetRequiredService(); + + client.Log += LogAsync; + services.GetRequiredService().Log += LogAsync; + + await client.LoginAsync(TokenType.Bot, Environment.GetEnvironmentVariable("token")); + await client.StartAsync(); + + await services.GetRequiredService().InitializeAsync(); + + await Task.Delay(-1); + } + + private Task LogAsync(LogMessage log) + { + Console.WriteLine(log.ToString()); + + return Task.CompletedTask; + } + + private IServiceProvider ConfigureServices() + { + return new ServiceCollection() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .BuildServiceProvider(); + } + } +} diff --git a/samples/02_commands_framework/Services/CommandHandlingService.cs b/samples/02_commands_framework/Services/CommandHandlingService.cs new file mode 100644 index 000000000..fc253eed3 --- /dev/null +++ b/samples/02_commands_framework/Services/CommandHandlingService.cs @@ -0,0 +1,49 @@ +using System; +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Discord; +using Discord.Commands; +using Discord.WebSocket; + +namespace _02_commands_framework.Services +{ + public class CommandHandlingService + { + private readonly CommandService _commands; + private readonly DiscordSocketClient _discord; + private readonly IServiceProvider _services; + + public CommandHandlingService(IServiceProvider services) + { + _commands = services.GetRequiredService(); + _discord = services.GetRequiredService(); + _services = services; + + _discord.MessageReceived += MessageReceivedAsync; + } + + public async Task InitializeAsync() + { + await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services); + } + + public async Task MessageReceivedAsync(SocketMessage rawMessage) + { + // Ignore system messages, or messages from other bots + if (!(rawMessage is SocketUserMessage message)) return; + if (message.Source != MessageSource.User) return; + + // This value holds the offset where the prefix ends + var argPos = 0; + if (!message.HasMentionPrefix(_discord.CurrentUser, ref argPos)) return; + + var context = new SocketCommandContext(_discord, message); + var result = await _commands.ExecuteAsync(context, argPos, _services); + + if (result.Error.HasValue && + result.Error.Value != CommandError.UnknownCommand) // it's bad practice to send 'unknown command' errors + await context.Channel.SendMessageAsync(result.ToString()); + } + } +} diff --git a/samples/02_commands_framework/Services/PictureService.cs b/samples/02_commands_framework/Services/PictureService.cs new file mode 100644 index 000000000..dda818cc3 --- /dev/null +++ b/samples/02_commands_framework/Services/PictureService.cs @@ -0,0 +1,20 @@ +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + +namespace _02_commands_framework.Services +{ + public class PictureService + { + private readonly HttpClient _http; + + public PictureService(HttpClient http) + => _http = http; + + public async Task GetCatPictureAsync() + { + var resp = await _http.GetAsync("https://cataas.com/cat"); + return await resp.Content.ReadAsStreamAsync(); + } + } +} From 6a7810b3a4d1050149681864d02d55ce722ed58f Mon Sep 17 00:00:00 2001 From: Alex Gravely Date: Sun, 29 Apr 2018 11:18:57 -0400 Subject: [PATCH 11/49] Fix Guild not being populated in SocketWebhookUser (#1044) --- src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs index 78a29639b..dd80648d2 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketWebhookUser.cs @@ -26,6 +26,7 @@ namespace Discord.WebSocket internal SocketWebhookUser(SocketGuild guild, ulong id, ulong webhookId) : base(guild.Discord, id) { + Guild = guild; WebhookId = webhookId; } internal static SocketWebhookUser Create(SocketGuild guild, ClientState state, Model model, ulong webhookId) From 660fec0cbc705b996838ba7a574fb36c02f35ce2 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Sun, 29 Apr 2018 17:24:24 +0200 Subject: [PATCH 12/49] Expose the internal entity type readers (#986) * Expose the internal entity type readers * Add BestMatch property to TypeReaderResult for easily accessing the parsed object --- src/Discord.Net.Commands/Readers/ChannelTypeReader.cs | 4 ++-- src/Discord.Net.Commands/Readers/MessageTypeReader.cs | 4 ++-- src/Discord.Net.Commands/Readers/RoleTypeReader.cs | 4 ++-- src/Discord.Net.Commands/Readers/UserTypeReader.cs | 4 ++-- src/Discord.Net.Commands/Results/TypeReaderResult.cs | 6 +++++- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs index 3136eb2cb..cd7a9d744 100644 --- a/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/ChannelTypeReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Discord.Commands { - internal class ChannelTypeReader : TypeReader + public class ChannelTypeReader : TypeReader where T : class, IChannel { public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs index fe576c3c6..a87cfbe43 100644 --- a/src/Discord.Net.Commands/Readers/MessageTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/MessageTypeReader.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Globalization; using System.Threading.Tasks; namespace Discord.Commands { - internal class MessageTypeReader : TypeReader + public class MessageTypeReader : TypeReader where T : class, IMessage { public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs index 703374c05..508624103 100644 --- a/src/Discord.Net.Commands/Readers/RoleTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/RoleTypeReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Discord.Commands { - internal class RoleTypeReader : TypeReader + public class RoleTypeReader : TypeReader where T : class, IRole { public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Readers/UserTypeReader.cs b/src/Discord.Net.Commands/Readers/UserTypeReader.cs index 8fc330d4c..425c2ccb7 100644 --- a/src/Discord.Net.Commands/Readers/UserTypeReader.cs +++ b/src/Discord.Net.Commands/Readers/UserTypeReader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace Discord.Commands { - internal class UserTypeReader : TypeReader + public class UserTypeReader : TypeReader where T : class, IUser { public override async Task ReadAsync(ICommandContext context, string input, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Results/TypeReaderResult.cs b/src/Discord.Net.Commands/Results/TypeReaderResult.cs index 68bc359c6..639ca3ac1 100644 --- a/src/Discord.Net.Commands/Results/TypeReaderResult.cs +++ b/src/Discord.Net.Commands/Results/TypeReaderResult.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; namespace Discord.Commands { @@ -30,6 +31,9 @@ namespace Discord.Commands public string ErrorReason { get; } public bool IsSuccess => !Error.HasValue; + public object BestMatch => IsSuccess + ? (Values.Count == 1 ? Values.Single().Value : Values.OrderByDescending(v => v.Score).First().Value) + : throw new InvalidOperationException("TypeReaderResult was not successful."); private TypeReaderResult(IReadOnlyCollection values, CommandError? error, string errorReason) { From e775853b1b26eaf80e6e76c295b44ca6241eaee7 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 4 May 2018 02:29:51 +0100 Subject: [PATCH 13/49] Expose VoiceServerUpdate events (#984) * Expose VoiceServerUpdate events * Amend based on feedback * Move this out of guild entity * Fix namespace issue * Adjust based on feedback #2 * Use cacheable instead * Change based on feedback --- .../BaseSocketClient.Events.cs | 9 +++++++- .../DiscordSocketClient.cs | 7 +++++++ .../SocketVoiceServer.cs | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/Discord.Net.WebSocket/SocketVoiceServer.cs diff --git a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs index e881a7855..c236b1045 100644 --- a/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs +++ b/src/Discord.Net.WebSocket/BaseSocketClient.Events.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord.WebSocket @@ -165,6 +165,13 @@ namespace Discord.WebSocket remove { _userVoiceStateUpdatedEvent.Remove(value); } } internal readonly AsyncEvent> _userVoiceStateUpdatedEvent = new AsyncEvent>(); + /// Fired when the bot connects to a Discord voice server. + public event Func VoiceServerUpdated + { + add { _voiceServerUpdatedEvent.Add(value); } + remove { _voiceServerUpdatedEvent.Remove(value); } + } + internal readonly AsyncEvent> _voiceServerUpdatedEvent = new AsyncEvent>(); /// Fired when the connected account is updated. public event Func CurrentUserUpdated { add { _selfUpdatedEvent.Add(value); } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 18413e840..95925291b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1466,6 +1466,12 @@ namespace Discord.WebSocket var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); + var cacheable = new Cacheable(guild, data.GuildId, guild != null, + async () => await ApiClient.GetGuildAsync(data.GuildId).ConfigureAwait(false) as IGuild); + + var voiceServer = new SocketVoiceServer(cacheable, data.GuildId, data.Endpoint, data.Token); + await TimedInvokeAsync(_voiceServerUpdatedEvent, nameof(UserVoiceStateUpdated), voiceServer).ConfigureAwait(false); + if (guild != null) { string endpoint = data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':')); @@ -1476,6 +1482,7 @@ namespace Discord.WebSocket await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); return; } + } break; diff --git a/src/Discord.Net.WebSocket/SocketVoiceServer.cs b/src/Discord.Net.WebSocket/SocketVoiceServer.cs new file mode 100644 index 000000000..9ab6afd3f --- /dev/null +++ b/src/Discord.Net.WebSocket/SocketVoiceServer.cs @@ -0,0 +1,21 @@ +using System.Diagnostics; + +namespace Discord.WebSocket +{ + [DebuggerDisplay(@"{DebuggerDisplay,nq}")] + public class SocketVoiceServer + { + public Cacheable Guild { get; private set; } + public string Endpoint { get; private set; } + public string Token { get; private set; } + + internal SocketVoiceServer(Cacheable guild, ulong guildId, string endpoint, string token) + { + Guild = guild; + Endpoint = endpoint; + Token = token; + } + + private string DebuggerDisplay => $"SocketVoiceServer ({Guild.Id})"; + } +} From 7cfed7ff67ac9aee517de4130d9ccf504791ed61 Mon Sep 17 00:00:00 2001 From: Alex Gravely Date: Thu, 3 May 2018 21:30:13 -0400 Subject: [PATCH 14/49] Fix nullref when passing null to GetShardIdFor. (#1049) --- src/Discord.Net.WebSocket/DiscordShardedClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index 45d76e61a..dad53b3b7 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -129,7 +129,7 @@ namespace Discord.WebSocket private int GetShardIdFor(ulong guildId) => (int)((guildId >> 22) % (uint)_totalShards); public int GetShardIdFor(IGuild guild) - => GetShardIdFor(guild.Id); + => GetShardIdFor(guild?.Id ?? 0); private DiscordSocketClient GetShardFor(ulong guildId) => GetShard(GetShardIdFor(guildId)); public DiscordSocketClient GetShardFor(IGuild guild) From bb4bb138460cc1e6bac53b4e838893dcfd812ce7 Mon Sep 17 00:00:00 2001 From: Finite Reality Date: Fri, 4 May 2018 11:42:54 +0100 Subject: [PATCH 15/49] Fix issues with #984, remove extraneous whitespace (#1051) - Removed unnecessary parameter in SocketVoiceServer - Moved SocketVoiceServer into Entities/Voice - Fixed a bug where trying to download the cached guild would throw - Fixed a potential bug where Discord might not give us a port when connecting to voice --- .../DiscordSocketClient.cs | 72 ++++++++++--------- .../{ => Entities/Voice}/SocketVoiceServer.cs | 2 +- 2 files changed, 40 insertions(+), 34 deletions(-) rename src/Discord.Net.WebSocket/{ => Entities/Voice}/SocketVoiceServer.cs (92%) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 95925291b..72b0b022b 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -62,9 +62,9 @@ namespace Discord.WebSocket internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; public override IReadOnlyCollection Guilds => State.Guilds; public override IReadOnlyCollection PrivateChannels => State.PrivateChannels; - public IReadOnlyCollection DMChannels + public IReadOnlyCollection DMChannels => State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray(); - public IReadOnlyCollection GroupChannels + public IReadOnlyCollection GroupChannels => State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray(); public override IReadOnlyCollection VoiceRegions => _voiceRegions.ToReadOnlyCollection(); @@ -89,11 +89,11 @@ namespace Discord.WebSocket _stateLock = new SemaphoreSlim(1, 1); _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); - _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, + _connection = new ConnectionManager(_stateLock, _gatewayLogger, config.ConnectionTimeout, OnConnectingAsync, OnDisconnectingAsync, x => ApiClient.Disconnected += x); _connection.Connected += () => TimedInvokeAsync(_connectedEvent, nameof(Connected)); _connection.Disconnected += (ex, recon) => TimedInvokeAsync(_disconnectedEvent, nameof(Disconnected), ex); - + _nextAudioId = 1; _connectionGroupLock = groupLock; _parentClient = parentClient; @@ -104,7 +104,7 @@ namespace Discord.WebSocket _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; @@ -136,7 +136,7 @@ namespace Discord.WebSocket ApiClient.Dispose(); } } - + internal override async Task OnLoginAsync(TokenType tokenType, string token) { if (_parentClient == null) @@ -154,11 +154,11 @@ namespace Discord.WebSocket _voiceRegions = ImmutableDictionary.Create(); } - public override async Task StartAsync() + public override async Task StartAsync() => await _connection.StartAsync().ConfigureAwait(false); - public override async Task StopAsync() + public override async Task StopAsync() => await _connection.StopAsync().ConfigureAwait(false); - + private async Task OnConnectingAsync() { if (_connectionGroupLock != null) @@ -181,11 +181,11 @@ namespace Discord.WebSocket //Wait for READY await _connection.WaitAsync().ConfigureAwait(false); - + await _gatewayLogger.DebugAsync("Sending Status").ConfigureAwait(false); await SendStatusAsync().ConfigureAwait(false); } - finally + finally { if (_connectionGroupLock != null) { @@ -230,22 +230,22 @@ namespace Discord.WebSocket } /// - public override async Task GetApplicationInfoAsync(RequestOptions options = null) + public override async Task GetApplicationInfoAsync(RequestOptions options = null) => _applicationInfo ?? (_applicationInfo = await ClientHelper.GetApplicationInfoAsync(this, options ?? RequestOptions.Default).ConfigureAwait(false)); /// - public override SocketGuild GetGuild(ulong id) - => State.GetGuild(id); + public override SocketGuild GetGuild(ulong id) + => State.GetGuild(id); /// - public override SocketChannel GetChannel(ulong id) + public override SocketChannel GetChannel(ulong id) => State.GetChannel(id); - + /// - public override SocketUser GetUser(ulong id) + public override SocketUser GetUser(ulong id) => State.GetUser(id); /// - public override SocketUser GetUser(string username, string discriminator) + public override SocketUser GetUser(string username, string discriminator) => State.Users.FirstOrDefault(x => x.Discriminator == discriminator && x.Username == username); internal SocketGlobalUser GetOrCreateUser(ClientState state, Discord.API.User model) { @@ -266,7 +266,7 @@ namespace Discord.WebSocket return user; }); } - internal void RemoveUser(ulong id) + internal void RemoveUser(ulong id) => State.RemoveUser(id); /// @@ -340,7 +340,7 @@ namespace Discord.WebSocket Activity = activity; await SendStatusAsync().ConfigureAwait(false); } - + private async Task SendStatusAsync() { if (CurrentUser == null) @@ -374,7 +374,7 @@ namespace Discord.WebSocket if (seq != null) _lastSeq = seq.Value; _lastMessageTime = Environment.TickCount; - + try { switch (opCode) @@ -390,7 +390,7 @@ namespace Discord.WebSocket case GatewayOpCode.Heartbeat: { await _gatewayLogger.DebugAsync("Received Heartbeat").ConfigureAwait(false); - + await ApiClient.SendHeartbeatAsync(_lastSeq).ConfigureAwait(false); } break; @@ -415,7 +415,7 @@ namespace Discord.WebSocket _sessionId = null; _lastSeq = 0; - + await ApiClient.SendIdentifyAsync(shardID: ShardId, totalShards: TotalShards).ConfigureAwait(false); } break; @@ -475,7 +475,7 @@ namespace Discord.WebSocket } else if (_connection.CancelToken.IsCancellationRequested) return; - + await TimedInvokeAsync(_readyEvent, nameof(Ready)).ConfigureAwait(false); await _gatewayLogger.InfoAsync("Ready").ConfigureAwait(false); }); @@ -514,7 +514,7 @@ namespace Discord.WebSocket if (guild != null) { guild.Update(State, data); - + if (_unavailableGuildCount != 0) _unavailableGuildCount--; await GuildAvailableAsync(guild).ConfigureAwait(false); @@ -1025,7 +1025,7 @@ namespace Discord.WebSocket SocketUser user = guild.GetUser(data.User.Id); if (user == null) - user = SocketUnknownUser.Create(this, State, data.User); + user = SocketUnknownUser.Create(this, State, data.User); await TimedInvokeAsync(_userBannedEvent, nameof(UserBanned), user, guild).ConfigureAwait(false); } else @@ -1325,7 +1325,7 @@ namespace Discord.WebSocket await TimedInvokeAsync(_userUpdatedEvent, nameof(UserUpdated), globalBefore, user).ConfigureAwait(false); } } - + var before = user.Clone(); user.Update(State, data, true); await TimedInvokeAsync(_guildMemberUpdatedEvent, nameof(GuildMemberUpdated), before, user).ConfigureAwait(false); @@ -1466,21 +1466,27 @@ namespace Discord.WebSocket var data = (payload as JToken).ToObject(_serializer); var guild = State.GetGuild(data.GuildId); - var cacheable = new Cacheable(guild, data.GuildId, guild != null, - async () => await ApiClient.GetGuildAsync(data.GuildId).ConfigureAwait(false) as IGuild); + var isCached = guild != null; + var cachedGuild = new Cacheable(guild, data.GuildId, isCached, + () => Task.FromResult(State.GetGuild(data.GuildId) as IGuild)); - var voiceServer = new SocketVoiceServer(cacheable, data.GuildId, data.Endpoint, data.Token); + var voiceServer = new SocketVoiceServer(cachedGuild, data.Endpoint, data.Token); await TimedInvokeAsync(_voiceServerUpdatedEvent, nameof(UserVoiceStateUpdated), voiceServer).ConfigureAwait(false); - if (guild != null) + if (isCached) { - string endpoint = data.Endpoint.Substring(0, data.Endpoint.LastIndexOf(':')); + var endpoint = data.Endpoint; + + //Only strip out the port if the endpoint contains it + var portBegin = endpoint.LastIndexOf(':'); + if (portBegin > 0) + endpoint = endpoint.Substring(0, portBegin); + var _ = guild.FinishConnectAudio(endpoint, data.Token).ConfigureAwait(false); } else { await UnknownGuildAsync(type, data.GuildId).ConfigureAwait(false); - return; } } diff --git a/src/Discord.Net.WebSocket/SocketVoiceServer.cs b/src/Discord.Net.WebSocket/Entities/Voice/SocketVoiceServer.cs similarity index 92% rename from src/Discord.Net.WebSocket/SocketVoiceServer.cs rename to src/Discord.Net.WebSocket/Entities/Voice/SocketVoiceServer.cs index 9ab6afd3f..57abf1d03 100644 --- a/src/Discord.Net.WebSocket/SocketVoiceServer.cs +++ b/src/Discord.Net.WebSocket/Entities/Voice/SocketVoiceServer.cs @@ -9,7 +9,7 @@ namespace Discord.WebSocket public string Endpoint { get; private set; } public string Token { get; private set; } - internal SocketVoiceServer(Cacheable guild, ulong guildId, string endpoint, string token) + internal SocketVoiceServer(Cacheable guild, string endpoint, string token) { Guild = guild; Endpoint = endpoint; From 97c893107b51de24a38ea03c2f8260030c8fc7f5 Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Mon, 7 May 2018 06:22:49 +0800 Subject: [PATCH 16/49] Implement GetBanAsync (#1056) --- .../Entities/Guilds/IGuild.cs | 22 +++++++++++++++++-- src/Discord.Net.Rest/DiscordRestApiClient.cs | 9 ++++++++ .../Entities/Guilds/GuildHelper.cs | 7 +++++- .../Entities/Guilds/RestGuild.cs | 12 +++++++++- .../Entities/Guilds/SocketGuild.cs | 10 +++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 2f0599d76..1761b3f88 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -66,6 +66,24 @@ namespace Discord /// Gets a collection of all users banned on this guild. Task> GetBansAsync(RequestOptions options = null); + /// + /// Gets a ban object for a banned user. + /// + /// The banned user. + /// + /// An awaitable containing the ban object, which contains the user information and the + /// reason for the ban; if the ban entry cannot be found. + /// + Task GetBanAsync(IUser user, RequestOptions options = null); + /// + /// Gets a ban object for a banned user. + /// + /// The snowflake identifier for the banned user. + /// + /// An awaitable containing the ban object, which contains the user information and the + /// reason for the ban; if the ban entry cannot be found. + /// + Task GetBanAsync(ulong userId, RequestOptions options = null); /// Bans the provided user from this guild and optionally prunes their recent messages. /// The number of days to remove messages from this user for - must be between [0, 7] Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null); @@ -135,4 +153,4 @@ namespace Discord /// Deletes an existing emote from this guild. Task DeleteEmoteAsync(GuildEmote emote, RequestOptions options = null); } -} \ No newline at end of file +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 042ae9970..fbf9896b8 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -800,6 +800,15 @@ namespace Discord.API var ids = new BucketIds(guildId: guildId); return await SendAsync>("GET", () => $"guilds/{guildId}/bans", ids, options: options).ConfigureAwait(false); } + public async Task GetGuildBanAsync(ulong guildId, ulong userId, RequestOptions options) + { + Preconditions.NotEqual(userId, 0, nameof(userId)); + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + options = RequestOptions.CreateOrClone(options); + + var ids = new BucketIds(guildId: guildId); + return await SendAsync("GET", () => $"guilds/{guildId}/bans/{userId}", ids, options: options).ConfigureAwait(false); + } public async Task CreateGuildBanAsync(ulong guildId, ulong userId, CreateGuildBanParams args, RequestOptions options = null) { Preconditions.NotEqual(guildId, 0, nameof(guildId)); diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 12fdb075d..e3fd1a8c3 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -1,4 +1,4 @@ -using Discord.API.Rest; +using Discord.API.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -111,6 +111,11 @@ namespace Discord.Rest var models = await client.ApiClient.GetGuildBansAsync(guild.Id, options).ConfigureAwait(false); return models.Select(x => RestBan.Create(client, x)).ToImmutableArray(); } + public static async Task GetBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, RequestOptions options) + { + var model = await client.ApiClient.GetGuildBanAsync(guild.Id, userId, options).ConfigureAwait(false); + return RestBan.Create(client, model); + } public static async Task AddBanAsync(IGuild guild, BaseDiscordClient client, ulong userId, int pruneDays, string reason, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 5d12731a6..a7123930a 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -140,6 +140,10 @@ namespace Discord.Rest //Bans public Task> GetBansAsync(RequestOptions options = null) => GuildHelper.GetBansAsync(this, Discord, options); + public Task GetBanAsync(IUser user, RequestOptions options = null) + => GuildHelper.GetBanAsync(this, Discord, user.Id, options); + public Task GetBanAsync(ulong userId, RequestOptions options = null) + => GuildHelper.GetBanAsync(this, Discord, userId, options); public Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) => GuildHelper.AddBanAsync(this, Discord, user.Id, pruneDays, reason, options); @@ -291,6 +295,12 @@ namespace Discord.Rest async Task> IGuild.GetBansAsync(RequestOptions options) => await GetBansAsync(options).ConfigureAwait(false); + /// + async Task IGuild.GetBanAsync(IUser user, RequestOptions options) + => await GetBanAsync(user, options).ConfigureAwait(false); + /// + async Task IGuild.GetBanAsync(ulong userId, RequestOptions options) + => await GetBanAsync(userId, options).ConfigureAwait(false); async Task> IGuild.GetChannelsAsync(CacheMode mode, RequestOptions options) { diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 259dae5a8..3dbd1160c 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -288,6 +288,10 @@ namespace Discord.WebSocket //Bans public Task> GetBansAsync(RequestOptions options = null) => GuildHelper.GetBansAsync(this, Discord, options); + public Task GetBanAsync(IUser user, RequestOptions options = null) + => GuildHelper.GetBanAsync(this, Discord, user.Id, options); + public Task GetBanAsync(ulong userId, RequestOptions options = null) + => GuildHelper.GetBanAsync(this, Discord, userId, options); public Task AddBanAsync(IUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) => GuildHelper.AddBanAsync(this, Discord, user.Id, pruneDays, reason, options); @@ -641,6 +645,12 @@ namespace Discord.WebSocket async Task> IGuild.GetBansAsync(RequestOptions options) => await GetBansAsync(options).ConfigureAwait(false); + /// + async Task IGuild.GetBanAsync(IUser user, RequestOptions options) + => await GetBanAsync(user, options).ConfigureAwait(false); + /// + async Task IGuild.GetBanAsync(ulong userId, RequestOptions options) + => await GetBanAsync(userId, options).ConfigureAwait(false); Task> IGuild.GetChannelsAsync(CacheMode mode, RequestOptions options) => Task.FromResult>(Channels); From 39dffe858584d0e9eed750bff6426e9562db4262 Mon Sep 17 00:00:00 2001 From: Finite Reality Date: Sun, 13 May 2018 01:46:07 +0100 Subject: [PATCH 17/49] Audit Logs implementation (#1055) * Copy audit logs impl from old branch and clean up I suck at using git, so I'm gonna use brute force. * Remove unnecessary TODOs Category channels do not provide any new information, and the other I forgot to remove beforehand * Add invite update data, clean up after feedback * Remove TODOs, add WebhookType enum for future use WebhookType is a future-use type, as currently audit logs are the only thing which may return it. --- src/Discord.Net.Core/DiscordConfig.cs | 7 +- .../Entities/AuditLogs/ActionType.cs | 50 ++++++++++++ .../Entities/AuditLogs/IAuditLogData.cs | 14 ++++ .../Entities/AuditLogs/IAuditLogEntry.cs | 34 ++++++++ .../Entities/Guilds/IGuild.cs | 4 + .../Entities/Webhooks/WebhookType.cs | 14 ++++ src/Discord.Net.Rest/API/Common/AuditLog.cs | 16 ++++ .../API/Common/AuditLogChange.cs | 17 ++++ .../API/Common/AuditLogEntry.cs | 26 ++++++ .../API/Common/AuditLogOptions.cs | 27 +++++++ .../API/Rest/GetAuditLogsParams.cs | 8 ++ src/Discord.Net.Rest/DiscordRestApiClient.cs | 20 +++++ .../Entities/AuditLogs/AuditLogHelper.cs | 58 ++++++++++++++ .../AuditLogs/DataTypes/BanAuditLogData.cs | 23 ++++++ .../DataTypes/ChannelCreateAuditLogData.cs | 52 ++++++++++++ .../DataTypes/ChannelDeleteAuditLogData.cs | 45 +++++++++++ .../AuditLogs/DataTypes/ChannelInfo.cs | 18 +++++ .../DataTypes/ChannelUpdateAuditLogData.cs | 45 +++++++++++ .../DataTypes/EmoteCreateAuditLogData.cs | 31 ++++++++ .../DataTypes/EmoteDeleteAuditLogData.cs | 28 +++++++ .../DataTypes/EmoteUpdateAuditLogData.cs | 31 ++++++++ .../Entities/AuditLogs/DataTypes/GuildInfo.cs | 32 ++++++++ .../DataTypes/GuildUpdateAuditLogData.cs | 79 +++++++++++++++++++ .../DataTypes/InviteCreateAuditLogData.cs | 55 +++++++++++++ .../DataTypes/InviteDeleteAuditLogData.cs | 55 +++++++++++++ .../AuditLogs/DataTypes/InviteInfo.cs | 20 +++++ .../DataTypes/InviteUpdateAuditLogData.cs | 46 +++++++++++ .../AuditLogs/DataTypes/KickAuditLogData.cs | 23 ++++++ .../DataTypes/MemberRoleAuditLogData.cs | 50 ++++++++++++ .../DataTypes/MemberUpdateAuditLogData.cs | 35 ++++++++ .../DataTypes/MessageDeleteAuditLogData.cs | 22 ++++++ .../DataTypes/OverwriteCreateAuditLogData.cs | 37 +++++++++ .../DataTypes/OverwriteDeleteAuditLogData.cs | 42 ++++++++++ .../DataTypes/OverwriteUpdateAuditLogData.cs | 44 +++++++++++ .../AuditLogs/DataTypes/PruneAuditLogData.cs | 22 ++++++ .../DataTypes/RoleCreateAuditLogData.cs | 47 +++++++++++ .../DataTypes/RoleDeleteAuditLogData.cs | 47 +++++++++++ .../Entities/AuditLogs/DataTypes/RoleInfo.cs | 21 +++++ .../DataTypes/RoleUpdateAuditLogData.cs | 62 +++++++++++++++ .../AuditLogs/DataTypes/UnbanAuditLogData.cs | 23 ++++++ .../DataTypes/WebhookCreateAuditLogData.cs | 44 +++++++++++ .../DataTypes/WebhookDeleteAuditLogData.cs | 46 +++++++++++ .../AuditLogs/DataTypes/WebhookInfo.cs | 16 ++++ .../DataTypes/WebhookUpdateAuditLogData.cs | 52 ++++++++++++ .../Entities/AuditLogs/RestAuditLogEntry.cs | 38 +++++++++ .../Entities/Guilds/GuildHelper.cs | 29 +++++++ .../Entities/Guilds/RestGuild.cs | 12 +++ .../Entities/Guilds/SocketGuild.cs | 12 +++ 48 files changed, 1576 insertions(+), 3 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs create mode 100644 src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs create mode 100644 src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs create mode 100644 src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs create mode 100644 src/Discord.Net.Rest/API/Common/AuditLog.cs create mode 100644 src/Discord.Net.Rest/API/Common/AuditLogChange.cs create mode 100644 src/Discord.Net.Rest/API/Common/AuditLogEntry.cs create mode 100644 src/Discord.Net.Rest/API/Common/AuditLogOptions.cs create mode 100644 src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index fd2fe92e8..1db3271e5 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -4,10 +4,10 @@ namespace Discord { public class DiscordConfig { - public const int APIVersion = 6; + public const int APIVersion = 6; public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? - typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ?? + typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ?? "Unknown"; public static string UserAgent { get; } = $"DiscordBot (https://github.com/RogueException/Discord.Net, v{Version})"; @@ -20,10 +20,11 @@ namespace Discord public const int MaxMessagesPerBatch = 100; public const int MaxUsersPerBatch = 1000; public const int MaxGuildsPerBatch = 100; + public const int MaxAuditLogEntriesPerBatch = 100; /// 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 Log event. public LogSeverity LogLevel { get; set; } = LogSeverity.Info; diff --git a/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs new file mode 100644 index 000000000..e5a4ff30a --- /dev/null +++ b/src/Discord.Net.Core/Entities/AuditLogs/ActionType.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// The action type within a + /// + public enum ActionType + { + GuildUpdated = 1, + + ChannelCreated = 10, + ChannelUpdated = 11, + ChannelDeleted = 12, + + OverwriteCreated = 13, + OverwriteUpdated = 14, + OverwriteDeleted = 15, + + Kick = 20, + Prune = 21, + Ban = 22, + Unban = 23, + + MemberUpdated = 24, + MemberRoleUpdated = 25, + + RoleCreated = 30, + RoleUpdated = 31, + RoleDeleted = 32, + + InviteCreated = 40, + InviteUpdated = 41, + InviteDeleted = 42, + + WebhookCreated = 50, + WebhookUpdated = 51, + WebhookDeleted = 52, + + EmojiCreated = 60, + EmojiUpdated = 61, + EmojiDeleted = 62, + + MessageDeleted = 72 + } +} diff --git a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs new file mode 100644 index 000000000..47aaffb26 --- /dev/null +++ b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogData.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// Represents data applied to an + /// + public interface IAuditLogData + { } +} diff --git a/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs new file mode 100644 index 000000000..b85730a1d --- /dev/null +++ b/src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// Represents an entry in an audit log + /// + public interface IAuditLogEntry : IEntity + { + /// + /// The action which occured to create this entry + /// + ActionType Action { get; } + + /// + /// The data for this entry. May be if no data was available. + /// + IAuditLogData Data { get; } + + /// + /// The user responsible for causing the changes + /// + IUser User { get; } + + /// + /// The reason behind the change. May be if no reason was provided. + /// + string Reason { get; } + } +} diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 1761b3f88..19d2ee81c 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -139,6 +139,10 @@ namespace Discord /// 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. Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null); + /// Gets the specified number of audit log entries for this guild. + Task> GetAuditLogAsync(int limit = DiscordConfig.MaxAuditLogEntriesPerBatch, + CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + /// Gets the webhook in this guild with the provided id, or null if not found. Task GetWebhookAsync(ulong id, RequestOptions options = null); /// Gets a collection of all webhooks for this guild. diff --git a/src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs b/src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs new file mode 100644 index 000000000..0ddfa393d --- /dev/null +++ b/src/Discord.Net.Core/Entities/Webhooks/WebhookType.cs @@ -0,0 +1,14 @@ +namespace Discord +{ + /// + /// Represents the type of a webhook. + /// + /// + /// This type is currently unused, and is only returned in audit log responses. + /// + public enum WebhookType + { + /// An incoming webhook + Incoming = 1 + } +} diff --git a/src/Discord.Net.Rest/API/Common/AuditLog.cs b/src/Discord.Net.Rest/API/Common/AuditLog.cs new file mode 100644 index 000000000..cd8ad147d --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/AuditLog.cs @@ -0,0 +1,16 @@ +using Newtonsoft.Json; + +namespace Discord.API +{ + internal class AuditLog + { + [JsonProperty("webhooks")] + public Webhook[] Webhooks { get; set; } + + [JsonProperty("users")] + public User[] Users { get; set; } + + [JsonProperty("audit_log_entries")] + public AuditLogEntry[] Entries { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Common/AuditLogChange.cs b/src/Discord.Net.Rest/API/Common/AuditLogChange.cs new file mode 100644 index 000000000..44e585021 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/AuditLogChange.cs @@ -0,0 +1,17 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Discord.API +{ + internal class AuditLogChange + { + [JsonProperty("key")] + public string ChangedProperty { get; set; } + + [JsonProperty("new_value")] + public JToken NewValue { get; set; } + + [JsonProperty("old_value")] + public JToken OldValue { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs b/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs new file mode 100644 index 000000000..80d9a9e97 --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/AuditLogEntry.cs @@ -0,0 +1,26 @@ +using Newtonsoft.Json; + +namespace Discord.API +{ + internal class AuditLogEntry + { + [JsonProperty("target_id")] + public ulong? TargetId { get; set; } + [JsonProperty("user_id")] + public ulong UserId { get; set; } + + [JsonProperty("changes")] + public AuditLogChange[] Changes { get; set; } + [JsonProperty("options")] + public AuditLogOptions Options { get; set; } + + [JsonProperty("id")] + public ulong Id { get; set; } + + [JsonProperty("action_type")] + public ActionType Action { get; set; } + + [JsonProperty("reason")] + public string Reason { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Common/AuditLogOptions.cs b/src/Discord.Net.Rest/API/Common/AuditLogOptions.cs new file mode 100644 index 000000000..65b401cce --- /dev/null +++ b/src/Discord.Net.Rest/API/Common/AuditLogOptions.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Discord.API +{ + internal class AuditLogOptions + { + //Message delete + [JsonProperty("count")] + public int? MessageDeleteCount { get; set; } + [JsonProperty("channel_id")] + public ulong? MessageDeleteChannelId { get; set; } + + //Prune + [JsonProperty("delete_member_days")] + public int? PruneDeleteMemberDays { get; set; } + [JsonProperty("members_removed")] + public int? PruneMembersRemoved { get; set; } + + //Overwrite Update + [JsonProperty("role_name")] + public string OverwriteRoleName { get; set; } + [JsonProperty("type")] + public string OverwriteType { get; set; } + [JsonProperty("id")] + public ulong? OverwriteTargetId { get; set; } + } +} diff --git a/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs b/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs new file mode 100644 index 000000000..ceabccbc8 --- /dev/null +++ b/src/Discord.Net.Rest/API/Rest/GetAuditLogsParams.cs @@ -0,0 +1,8 @@ +namespace Discord.API.Rest +{ + class GetAuditLogsParams + { + public Optional Limit { get; set; } + public Optional BeforeEntryId { get; set; } + } +} diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index fbf9896b8..8b641fb6a 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1206,6 +1206,26 @@ namespace Discord.API return await SendAsync>("GET", () => $"guilds/{guildId}/regions", ids, options: options).ConfigureAwait(false); } + //Audit logs + public async Task GetAuditLogsAsync(ulong guildId, GetAuditLogsParams args, RequestOptions options = null) + { + Preconditions.NotEqual(guildId, 0, nameof(guildId)); + Preconditions.NotNull(args, nameof(args)); + options = RequestOptions.CreateOrClone(options); + + int limit = args.Limit.GetValueOrDefault(int.MaxValue); + + var ids = new BucketIds(guildId: guildId); + Expression> endpoint; + + if (args.BeforeEntryId.IsSpecified) + endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}&before={args.BeforeEntryId.Value}"; + else + endpoint = () => $"guilds/{guildId}/audit-logs?limit={limit}"; + + return await SendAsync("GET", endpoint, ids, options: options).ConfigureAwait(false); + } + //Webhooks public async Task CreateWebhookAsync(ulong channelId, CreateWebhookParams args, RequestOptions options = null) { diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs b/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs new file mode 100644 index 000000000..7936343f3 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/AuditLogHelper.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + internal static class AuditLogHelper + { + private static readonly Dictionary> CreateMapping + = new Dictionary>() + { + [ActionType.GuildUpdated] = GuildUpdateAuditLogData.Create, + + [ActionType.ChannelCreated] = ChannelCreateAuditLogData.Create, + [ActionType.ChannelUpdated] = ChannelUpdateAuditLogData.Create, + [ActionType.ChannelDeleted] = ChannelDeleteAuditLogData.Create, + + [ActionType.OverwriteCreated] = OverwriteCreateAuditLogData.Create, + [ActionType.OverwriteUpdated] = OverwriteUpdateAuditLogData.Create, + [ActionType.OverwriteDeleted] = OverwriteDeleteAuditLogData.Create, + + [ActionType.Kick] = KickAuditLogData.Create, + [ActionType.Prune] = PruneAuditLogData.Create, + [ActionType.Ban] = BanAuditLogData.Create, + [ActionType.Unban] = UnbanAuditLogData.Create, + [ActionType.MemberUpdated] = MemberUpdateAuditLogData.Create, + [ActionType.MemberRoleUpdated] = MemberRoleAuditLogData.Create, + + [ActionType.RoleCreated] = RoleCreateAuditLogData.Create, + [ActionType.RoleUpdated] = RoleUpdateAuditLogData.Create, + [ActionType.RoleDeleted] = RoleDeleteAuditLogData.Create, + + [ActionType.InviteCreated] = InviteCreateAuditLogData.Create, + [ActionType.InviteUpdated] = InviteUpdateAuditLogData.Create, + [ActionType.InviteDeleted] = InviteDeleteAuditLogData.Create, + + [ActionType.WebhookCreated] = WebhookCreateAuditLogData.Create, + [ActionType.WebhookUpdated] = WebhookUpdateAuditLogData.Create, + [ActionType.WebhookDeleted] = WebhookDeleteAuditLogData.Create, + + [ActionType.EmojiCreated] = EmoteCreateAuditLogData.Create, + [ActionType.EmojiUpdated] = EmoteUpdateAuditLogData.Create, + [ActionType.EmojiDeleted] = EmoteDeleteAuditLogData.Create, + + [ActionType.MessageDeleted] = MessageDeleteAuditLogData.Create, + }; + + public static IAuditLogData CreateData(BaseDiscordClient discord, Model log, EntryModel entry) + { + if (CreateMapping.TryGetValue(entry.Action, out var func)) + return func(discord, log, entry); + + return null; + } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs new file mode 100644 index 000000000..4b9d5875f --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/BanAuditLogData.cs @@ -0,0 +1,23 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class BanAuditLogData : IAuditLogData + { + private BanAuditLogData(IUser user) + { + Target = user; + } + + internal static BanAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); + return new BanAuditLogData(RestUser.Create(discord, userInfo)); + } + + public IUser Target { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs new file mode 100644 index 000000000..ef4787295 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class ChannelCreateAuditLogData : IAuditLogData + { + private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) + { + ChannelId = id; + ChannelName = name; + ChannelType = type; + Overwrites = overwrites; + } + + internal static ChannelCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + var overwrites = new List(); + + var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites"); + var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var type = typeModel.NewValue.ToObject(); + var name = nameModel.NewValue.ToObject(); + + foreach (var overwrite in overwritesModel.NewValue) + { + var deny = overwrite.Value("deny"); + var _type = overwrite.Value("type"); + var id = overwrite.Value("id"); + var allow = overwrite.Value("allow"); + + PermissionTarget permType = _type == "member" ? PermissionTarget.User : PermissionTarget.Role; + + overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny))); + } + + return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection()); + } + + public ulong ChannelId { get; } + public string ChannelName { get; } + public ChannelType ChannelType { get; } + public IReadOnlyCollection Overwrites { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs new file mode 100644 index 000000000..4816ce770 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class ChannelDeleteAuditLogData : IAuditLogData + { + private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) + { + ChannelId = id; + ChannelName = name; + ChannelType = type; + Overwrites = overwrites; + } + + internal static ChannelDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites"); + var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var overwrites = overwritesModel.OldValue.ToObject() + .Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny))) + .ToList(); + var type = typeModel.OldValue.ToObject(); + var name = nameModel.OldValue.ToObject(); + var id = entry.TargetId.Value; + + return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); + } + + public ulong ChannelId { get; } + public string ChannelName { get; } + public ChannelType ChannelType { get; } + public IReadOnlyCollection Overwrites { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs new file mode 100644 index 000000000..e2d6064a9 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs @@ -0,0 +1,18 @@ +namespace Discord.Rest +{ + public struct ChannelInfo + { + internal ChannelInfo(string name, string topic, int? bitrate, int? limit) + { + Name = name; + Topic = topic; + Bitrate = bitrate; + UserLimit = limit; + } + + public string Name { get; } + public string Topic { get; } + public int? Bitrate { get; } + public int? UserLimit { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs new file mode 100644 index 000000000..f3403138d --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs @@ -0,0 +1,45 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class ChannelUpdateAuditLogData : IAuditLogData + { + private ChannelUpdateAuditLogData(ulong id, ChannelInfo before, ChannelInfo after) + { + ChannelId = id; + Before = before; + After = after; + } + + internal static ChannelUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var topicModel = changes.FirstOrDefault(x => x.ChangedProperty == "topic"); + var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); + var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit"); + + string oldName = nameModel?.OldValue?.ToObject(), + newName = nameModel?.NewValue?.ToObject(); + string oldTopic = topicModel?.OldValue?.ToObject(), + newTopic = topicModel?.NewValue?.ToObject(); + int? oldBitrate = bitrateModel?.OldValue?.ToObject(), + newBitrate = bitrateModel?.NewValue?.ToObject(); + int? oldLimit = userLimitModel?.OldValue?.ToObject(), + newLimit = userLimitModel?.NewValue?.ToObject(); + + var before = new ChannelInfo(oldName, oldTopic, oldBitrate, oldLimit); + var after = new ChannelInfo(newName, newTopic, newBitrate, newLimit); + + return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); + } + + public ulong ChannelId { get; } + public ChannelInfo Before { get; set; } + public ChannelInfo After { get; set; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs new file mode 100644 index 000000000..5d1ef8463 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class EmoteCreateAuditLogData : IAuditLogData + { + private EmoteCreateAuditLogData(ulong id, string name) + { + EmoteId = id; + Name = name; + } + + internal static EmoteCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var emoteName = change.NewValue?.ToObject(); + return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName); + } + + public ulong EmoteId { get; } + public string Name { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs new file mode 100644 index 000000000..d0a11191f --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs @@ -0,0 +1,28 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class EmoteDeleteAuditLogData : IAuditLogData + { + private EmoteDeleteAuditLogData(ulong id, string name) + { + EmoteId = id; + Name = name; + } + + internal static EmoteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var emoteName = change.OldValue?.ToObject(); + + return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName); + } + + public ulong EmoteId { get; } + public string Name { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs new file mode 100644 index 000000000..60020bcaa --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs @@ -0,0 +1,31 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class EmoteUpdateAuditLogData : IAuditLogData + { + private EmoteUpdateAuditLogData(ulong id, string oldName, string newName) + { + EmoteId = id; + OldName = oldName; + NewName = newName; + } + + internal static EmoteUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var newName = change.NewValue?.ToObject(); + var oldName = change.OldValue?.ToObject(); + + return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName); + } + + public ulong EmoteId { get; } + public string NewName { get; } + public string OldName { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs new file mode 100644 index 000000000..90865ef72 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs @@ -0,0 +1,32 @@ +namespace Discord.Rest +{ + public struct GuildInfo + { + internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs, + ulong? afkChannel, string name, string region, string icon, + VerificationLevel? verification, IUser owner, MfaLevel? mfa, int? filter) + { + AfkTimeout = afkTimeout; + DefaultMessageNotifications = defaultNotifs; + AfkChannelId = afkChannel; + Name = name; + RegionId = region; + IconHash = icon; + VerificationLevel = verification; + Owner = owner; + MfaLevel = mfa; + ContentFilterLevel = filter; + } + + public int? AfkTimeout { get; } + public DefaultMessageNotifications? DefaultMessageNotifications { get; } + public ulong? AfkChannelId { get; } + public string Name { get; } + public string RegionId { get; } + public string IconHash { get; } + public VerificationLevel? VerificationLevel { get; } + public IUser Owner { get; } + public MfaLevel? MfaLevel { get; } + public int? ContentFilterLevel { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs new file mode 100644 index 000000000..08550ed7a --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs @@ -0,0 +1,79 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class GuildUpdateAuditLogData : IAuditLogData + { + private GuildUpdateAuditLogData(GuildInfo before, GuildInfo after) + { + Before = before; + After = after; + } + + internal static GuildUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var afkTimeoutModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var defaultMessageNotificationsModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var afkChannelModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var regionIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var iconHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var verificationLevelModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var ownerIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var mfaLevelModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + var contentFilterModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout"); + + int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject(), + newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject(); + DefaultMessageNotifications? oldDefaultMessageNotifications = defaultMessageNotificationsModel?.OldValue?.ToObject(), + newDefaultMessageNotifications = defaultMessageNotificationsModel?.NewValue?.ToObject(); + ulong? oldAfkChannelId = afkChannelModel?.OldValue?.ToObject(), + newAfkChannelId = afkChannelModel?.NewValue?.ToObject(); + string oldName = nameModel?.OldValue?.ToObject(), + newName = nameModel?.NewValue?.ToObject(); + string oldRegionId = regionIdModel?.OldValue?.ToObject(), + newRegionId = regionIdModel?.NewValue?.ToObject(); + string oldIconHash = iconHashModel?.OldValue?.ToObject(), + newIconHash = iconHashModel?.NewValue?.ToObject(); + VerificationLevel? oldVerificationLevel = verificationLevelModel?.OldValue?.ToObject(), + newVerificationLevel = verificationLevelModel?.NewValue?.ToObject(); + ulong? oldOwnerId = ownerIdModel?.OldValue?.ToObject(), + newOwnerId = ownerIdModel?.NewValue?.ToObject(); + MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject(), + newMfaLevel = mfaLevelModel?.NewValue?.ToObject(); + int? oldContentFilter = contentFilterModel?.OldValue?.ToObject(), + newContentFilter = contentFilterModel?.NewValue?.ToObject(); + + IUser oldOwner = null; + if (oldOwnerId != null) + { + var oldOwnerInfo = log.Users.FirstOrDefault(x => x.Id == oldOwnerId.Value); + oldOwner = RestUser.Create(discord, oldOwnerInfo); + } + + IUser newOwner = null; + if (newOwnerId != null) + { + var newOwnerInfo = log.Users.FirstOrDefault(x => x.Id == newOwnerId.Value); + newOwner = RestUser.Create(discord, newOwnerInfo); + } + + var before = new GuildInfo(oldAfkTimeout, oldDefaultMessageNotifications, + oldAfkChannelId, oldName, oldRegionId, oldIconHash, oldVerificationLevel, oldOwner, + oldMfaLevel, oldContentFilter); + var after = new GuildInfo(newAfkTimeout, newDefaultMessageNotifications, + newAfkChannelId, newName, newRegionId, newIconHash, newVerificationLevel, newOwner, + newMfaLevel, newContentFilter); + + return new GuildUpdateAuditLogData(before, after); + } + + public GuildInfo Before { get; } + public GuildInfo After { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs new file mode 100644 index 000000000..292715420 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs @@ -0,0 +1,55 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class InviteCreateAuditLogData : IAuditLogData + { + private InviteCreateAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) + { + MaxAge = maxAge; + Code = code; + Temporary = temporary; + Creator = inviter; + ChannelId = channelId; + Uses = uses; + MaxUses = maxUses; + } + + internal static InviteCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var maxAgeModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_age"); + var codeModel = changes.FirstOrDefault(x => x.ChangedProperty == "code"); + var temporaryModel = changes.FirstOrDefault(x => x.ChangedProperty == "temporary"); + var inviterIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "inviter_id"); + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); + var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); + + var maxAge = maxAgeModel.NewValue.ToObject(); + var code = codeModel.NewValue.ToObject(); + var temporary = temporaryModel.NewValue.ToObject(); + var inviterId = inviterIdModel.NewValue.ToObject(); + var channelId = channelIdModel.NewValue.ToObject(); + var uses = usesModel.NewValue.ToObject(); + var maxUses = maxUsesModel.NewValue.ToObject(); + + var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); + var inviter = RestUser.Create(discord, inviterInfo); + + return new InviteCreateAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); + } + + public int MaxAge { get; } + public string Code { get; } + public bool Temporary { get; } + public IUser Creator { get; } + public ulong ChannelId { get; } + public int Uses { get; } + public int MaxUses { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs new file mode 100644 index 000000000..1dc6d518b --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs @@ -0,0 +1,55 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class InviteDeleteAuditLogData : IAuditLogData + { + private InviteDeleteAuditLogData(int maxAge, string code, bool temporary, IUser inviter, ulong channelId, int uses, int maxUses) + { + MaxAge = maxAge; + Code = code; + Temporary = temporary; + Creator = inviter; + ChannelId = channelId; + Uses = uses; + MaxUses = maxUses; + } + + internal static InviteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var maxAgeModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_age"); + var codeModel = changes.FirstOrDefault(x => x.ChangedProperty == "code"); + var temporaryModel = changes.FirstOrDefault(x => x.ChangedProperty == "temporary"); + var inviterIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "inviter_id"); + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); + var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); + + var maxAge = maxAgeModel.OldValue.ToObject(); + var code = codeModel.OldValue.ToObject(); + var temporary = temporaryModel.OldValue.ToObject(); + var inviterId = inviterIdModel.OldValue.ToObject(); + var channelId = channelIdModel.OldValue.ToObject(); + var uses = usesModel.OldValue.ToObject(); + var maxUses = maxUsesModel.OldValue.ToObject(); + + var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); + var inviter = RestUser.Create(discord, inviterInfo); + + return new InviteDeleteAuditLogData(maxAge, code, temporary, inviter, channelId, uses, maxUses); + } + + public int MaxAge { get; } + public string Code { get; } + public bool Temporary { get; } + public IUser Creator { get; } + public ulong ChannelId { get; } + public int Uses { get; } + public int MaxUses { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs new file mode 100644 index 000000000..c9840f6cc --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteInfo.cs @@ -0,0 +1,20 @@ +namespace Discord.Rest +{ + public struct InviteInfo + { + internal InviteInfo(int? maxAge, string code, bool? temporary, ulong? channelId, int? maxUses) + { + MaxAge = maxAge; + Code = code; + Temporary = temporary; + ChannelId = channelId; + MaxUses = maxUses; + } + + public int? MaxAge { get; } + public string Code { get; } + public bool? Temporary { get; } + public ulong? ChannelId { get; } + public int? MaxUses { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs new file mode 100644 index 000000000..b932cfbfc --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs @@ -0,0 +1,46 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class InviteUpdateAuditLogData : IAuditLogData + { + private InviteUpdateAuditLogData(InviteInfo before, InviteInfo after) + { + Before = before; + After = after; + } + + internal static InviteUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var maxAgeModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_age"); + var codeModel = changes.FirstOrDefault(x => x.ChangedProperty == "code"); + var temporaryModel = changes.FirstOrDefault(x => x.ChangedProperty == "temporary"); + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); + + int? oldMaxAge = maxAgeModel?.OldValue?.ToObject(), + newMaxAge = maxAgeModel?.NewValue?.ToObject(); + string oldCode = codeModel?.OldValue?.ToObject(), + newCode = codeModel?.NewValue?.ToObject(); + bool? oldTemporary = temporaryModel?.OldValue?.ToObject(), + newTemporary = temporaryModel?.NewValue?.ToObject(); + ulong? oldChannelId = channelIdModel?.OldValue?.ToObject(), + newChannelId = channelIdModel?.NewValue?.ToObject(); + int? oldMaxUses = maxUsesModel?.OldValue?.ToObject(), + newMaxUses = maxUsesModel?.NewValue?.ToObject(); + + var before = new InviteInfo(oldMaxAge, oldCode, oldTemporary, oldChannelId, oldMaxUses); + var after = new InviteInfo(newMaxAge, newCode, newTemporary, newChannelId, newMaxUses); + + return new InviteUpdateAuditLogData(before, after); + } + + public InviteInfo Before { get; } + public InviteInfo After { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs new file mode 100644 index 000000000..41b5526b8 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/KickAuditLogData.cs @@ -0,0 +1,23 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class KickAuditLogData : IAuditLogData + { + private KickAuditLogData(RestUser user) + { + Target = user; + } + + internal static KickAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); + return new KickAuditLogData(RestUser.Create(discord, userInfo)); + } + + public IUser Target { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs new file mode 100644 index 000000000..b0f0a1fe1 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class MemberRoleAuditLogData : IAuditLogData + { + private MemberRoleAuditLogData(IReadOnlyCollection roles, IUser target) + { + Roles = roles; + Target = target; + } + + internal static MemberRoleAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var roleInfos = changes.SelectMany(x => x.NewValue.ToObject(), + (model, role) => new { model.ChangedProperty, Role = role }) + .Select(x => new RoleInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) + .ToList(); + + var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); + var user = RestUser.Create(discord, userInfo); + + return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user); + } + + public IReadOnlyCollection Roles { get; } + public IUser Target { get; } + + public struct RoleInfo + { + internal RoleInfo(string name, ulong roleId, bool added) + { + Name = name; + RoleId = roleId; + Added = added; + } + + public string Name { get; } + public ulong RoleId { get; } + public bool Added { get; } + } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs new file mode 100644 index 000000000..38f078848 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs @@ -0,0 +1,35 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; +using ChangeModel = Discord.API.AuditLogChange; + +namespace Discord.Rest +{ + public class MemberUpdateAuditLogData : IAuditLogData + { + private MemberUpdateAuditLogData(IUser target, string newNick, string oldNick) + { + Target = target; + NewNick = newNick; + OldNick = oldNick; + } + + internal static MemberUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "nick"); + + var newNick = changes.NewValue?.ToObject(); + var oldNick = changes.OldValue?.ToObject(); + + var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); + var user = RestUser.Create(discord, targetInfo); + + return new MemberUpdateAuditLogData(user, newNick, oldNick); + } + + public IUser Target { get; } + public string NewNick { get; } + public string OldNick { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs new file mode 100644 index 000000000..3949cdd68 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MessageDeleteAuditLogData.cs @@ -0,0 +1,22 @@ +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class MessageDeleteAuditLogData : IAuditLogData + { + private MessageDeleteAuditLogData(ulong channelId, int count) + { + ChannelId = channelId; + MessageCount = count; + } + + internal static MessageDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + return new MessageDeleteAuditLogData(entry.Options.MessageDeleteChannelId.Value, entry.Options.MessageDeleteCount.Value); + } + + public int MessageCount { get; } + public ulong ChannelId { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs new file mode 100644 index 000000000..d58488136 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs @@ -0,0 +1,37 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class OverwriteCreateAuditLogData : IAuditLogData + { + private OverwriteCreateAuditLogData(Overwrite overwrite) + { + Overwrite = overwrite; + } + + internal static OverwriteCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); + var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); + + var deny = denyModel.NewValue.ToObject(); + var allow = allowModel.NewValue.ToObject(); + + var permissions = new OverwritePermissions(allow, deny); + + var id = entry.Options.OverwriteTargetId.Value; + var type = entry.Options.OverwriteType; + + PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role; + + return new OverwriteCreateAuditLogData(new Overwrite(id, target, permissions)); + } + + public Overwrite Overwrite { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs new file mode 100644 index 000000000..445c2e302 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; +using ChangeModel = Discord.API.AuditLogChange; +using OptionModel = Discord.API.AuditLogOptions; + +namespace Discord.Rest +{ + public class OverwriteDeleteAuditLogData : IAuditLogData + { + private OverwriteDeleteAuditLogData(Overwrite deletedOverwrite) + { + Overwrite = deletedOverwrite; + } + + internal static OverwriteDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); + var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); + var idModel = changes.FirstOrDefault(x => x.ChangedProperty == "id"); + var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); + + var deny = denyModel.OldValue.ToObject(); + var type = typeModel.OldValue.ToObject(); + var id = idModel.OldValue.ToObject(); + var allow = allowModel.OldValue.ToObject(); + + PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role; + + return new OverwriteDeleteAuditLogData(new Overwrite(id, target, new OverwritePermissions(allow, deny))); + } + + public Overwrite Overwrite { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs new file mode 100644 index 000000000..d000146c3 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs @@ -0,0 +1,44 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class OverwriteUpdateAuditLogData : IAuditLogData + { + private OverwriteUpdateAuditLogData(OverwritePermissions before, OverwritePermissions after, ulong targetId, PermissionTarget targetType) + { + OldPermissions = before; + NewPermissions = after; + OverwriteTargetId = targetId; + OverwriteType = targetType; + } + + internal static OverwriteUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); + var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); + + var beforeAllow = allowModel?.OldValue?.ToObject(); + var afterAllow = allowModel?.NewValue?.ToObject(); + var beforeDeny = denyModel?.OldValue?.ToObject(); + var afterDeny = denyModel?.OldValue?.ToObject(); + + var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0); + var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0); + + PermissionTarget target = entry.Options.OverwriteType == "member" ? PermissionTarget.User : PermissionTarget.Role; + + return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, target); + } + + public OverwritePermissions OldPermissions { get; } + public OverwritePermissions NewPermissions { get; } + + public ulong OverwriteTargetId { get; } + public PermissionTarget OverwriteType { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs new file mode 100644 index 000000000..0005e304d --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/PruneAuditLogData.cs @@ -0,0 +1,22 @@ +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class PruneAuditLogData : IAuditLogData + { + private PruneAuditLogData(int pruneDays, int membersRemoved) + { + PruneDays = pruneDays; + MembersRemoved = membersRemoved; + } + + internal static PruneAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + return new PruneAuditLogData(entry.Options.PruneDeleteMemberDays.Value, entry.Options.PruneMembersRemoved.Value); + } + + public int PruneDays { get; } + public int MembersRemoved { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs new file mode 100644 index 000000000..aa951d6e7 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs @@ -0,0 +1,47 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class RoleCreateAuditLogData : IAuditLogData + { + private RoleCreateAuditLogData(ulong id, RoleInfo props) + { + RoleId = id; + Properties = props; + } + + internal static RoleCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var colorModel = changes.FirstOrDefault(x => x.ChangedProperty == "color"); + var mentionableModel = changes.FirstOrDefault(x => x.ChangedProperty == "mentionable"); + var hoistModel = changes.FirstOrDefault(x => x.ChangedProperty == "hoist"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); + + uint? colorRaw = colorModel?.NewValue?.ToObject(); + bool? mentionable = mentionableModel?.NewValue?.ToObject(); + bool? hoist = hoistModel?.NewValue?.ToObject(); + string name = nameModel?.NewValue?.ToObject(); + ulong? permissionsRaw = permissionsModel?.NewValue?.ToObject(); + + Color? color = null; + GuildPermissions? permissions = null; + + if (colorRaw.HasValue) + color = new Color(colorRaw.Value); + if (permissionsRaw.HasValue) + permissions = new GuildPermissions(permissionsRaw.Value); + + return new RoleCreateAuditLogData(entry.TargetId.Value, + new RoleInfo(color, mentionable, hoist, name, permissions)); + } + + public ulong RoleId { get; } + public RoleInfo Properties { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs new file mode 100644 index 000000000..e90d70d4d --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs @@ -0,0 +1,47 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class RoleDeleteAuditLogData : IAuditLogData + { + private RoleDeleteAuditLogData(ulong id, RoleInfo props) + { + RoleId = id; + Properties = props; + } + + internal static RoleDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var colorModel = changes.FirstOrDefault(x => x.ChangedProperty == "color"); + var mentionableModel = changes.FirstOrDefault(x => x.ChangedProperty == "mentionable"); + var hoistModel = changes.FirstOrDefault(x => x.ChangedProperty == "hoist"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); + + uint? colorRaw = colorModel?.OldValue?.ToObject(); + bool? mentionable = mentionableModel?.OldValue?.ToObject(); + bool? hoist = hoistModel?.OldValue?.ToObject(); + string name = nameModel?.OldValue?.ToObject(); + ulong? permissionsRaw = permissionsModel?.OldValue?.ToObject(); + + Color? color = null; + GuildPermissions? permissions = null; + + if (colorRaw.HasValue) + color = new Color(colorRaw.Value); + if (permissionsRaw.HasValue) + permissions = new GuildPermissions(permissionsRaw.Value); + + return new RoleDeleteAuditLogData(entry.TargetId.Value, + new RoleInfo(color, mentionable, hoist, name, permissions)); + } + + public ulong RoleId { get; } + public RoleInfo Properties { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs new file mode 100644 index 000000000..2208990e6 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs @@ -0,0 +1,21 @@ +namespace Discord.Rest +{ + public struct RoleInfo + { + internal RoleInfo(Color? color, bool? mentionable, bool? hoist, string name, + GuildPermissions? permissions) + { + Color = color; + Mentionable = mentionable; + Hoist = hoist; + Name = name; + Permissions = permissions; + } + + public Color? Color { get; } + public bool? Mentionable { get; } + public bool? Hoist { get; } + public string Name { get; } + public GuildPermissions? Permissions { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs new file mode 100644 index 000000000..be484e2d5 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs @@ -0,0 +1,62 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class RoleUpdateAuditLogData : IAuditLogData + { + private RoleUpdateAuditLogData(ulong id, RoleInfo oldProps, RoleInfo newProps) + { + RoleId = id; + Before = oldProps; + After = newProps; + } + + internal static RoleUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var colorModel = changes.FirstOrDefault(x => x.ChangedProperty == "color"); + var mentionableModel = changes.FirstOrDefault(x => x.ChangedProperty == "mentionable"); + var hoistModel = changes.FirstOrDefault(x => x.ChangedProperty == "hoist"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); + + uint? oldColorRaw = colorModel?.OldValue?.ToObject(), + newColorRaw = colorModel?.NewValue?.ToObject(); + bool? oldMentionable = mentionableModel?.OldValue?.ToObject(), + newMentionable = mentionableModel?.NewValue?.ToObject(); + bool? oldHoist = hoistModel?.OldValue?.ToObject(), + newHoist = hoistModel?.NewValue?.ToObject(); + string oldName = nameModel?.OldValue?.ToObject(), + newName = nameModel?.NewValue?.ToObject(); + ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject(), + newPermissionsRaw = permissionsModel?.OldValue?.ToObject(); + + Color? oldColor = null, + newColor = null; + GuildPermissions? oldPermissions = null, + newPermissions = null; + + if (oldColorRaw.HasValue) + oldColor = new Color(oldColorRaw.Value); + if (newColorRaw.HasValue) + newColor = new Color(newColorRaw.Value); + if (oldPermissionsRaw.HasValue) + oldPermissions = new GuildPermissions(oldPermissionsRaw.Value); + if (newPermissionsRaw.HasValue) + newPermissions = new GuildPermissions(newPermissionsRaw.Value); + + var oldProps = new RoleInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions); + var newProps = new RoleInfo(newColor, newMentionable, newHoist, newName, newPermissions); + + return new RoleUpdateAuditLogData(entry.TargetId.Value, oldProps, newProps); + } + + public ulong RoleId { get; } + public RoleInfo Before { get; } + public RoleInfo After { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs new file mode 100644 index 000000000..c94f18271 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/UnbanAuditLogData.cs @@ -0,0 +1,23 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class UnbanAuditLogData : IAuditLogData + { + private UnbanAuditLogData(IUser user) + { + Target = user; + } + + internal static UnbanAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); + return new UnbanAuditLogData(RestUser.Create(discord, userInfo)); + } + + public IUser Target { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs new file mode 100644 index 000000000..1ae45fb8c --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs @@ -0,0 +1,44 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class WebhookCreateAuditLogData : IAuditLogData + { + private WebhookCreateAuditLogData(IWebhook webhook, WebhookType type, string name, ulong channelId) + { + Webhook = webhook; + Name = name; + Type = type; + ChannelId = channelId; + } + + internal static WebhookCreateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + + var channelId = channelIdModel.NewValue.ToObject(); + var type = typeModel.NewValue.ToObject(); + var name = nameModel.NewValue.ToObject(); + + var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); + var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo); + + return new WebhookCreateAuditLogData(webhook, type, name, channelId); + } + + //Corresponds to the *current* data + public IWebhook Webhook { get; } + + //Corresponds to the *audit log* data + public WebhookType Type { get; } + public string Name { get; } + public ulong ChannelId { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs new file mode 100644 index 000000000..4133d5dff --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class WebhookDeleteAuditLogData : IAuditLogData + { + private WebhookDeleteAuditLogData(ulong id, ulong channel, WebhookType type, string name, string avatar) + { + WebhookId = id; + ChannelId = channel; + Name = name; + Type = type; + Avatar = avatar; + } + + internal static WebhookDeleteAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); + + var channelId = channelIdModel.OldValue.ToObject(); + var type = typeModel.OldValue.ToObject(); + var name = nameModel.OldValue.ToObject(); + var avatarHash = avatarHashModel?.OldValue?.ToObject(); + + return new WebhookDeleteAuditLogData(entry.TargetId.Value, channelId, type, name, avatarHash); + } + + public ulong WebhookId { get; } + public ulong ChannelId { get; } + public WebhookType Type { get; } + public string Name { get; } + public string Avatar { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookInfo.cs new file mode 100644 index 000000000..26975cc7c --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookInfo.cs @@ -0,0 +1,16 @@ +namespace Discord.Rest +{ + public struct WebhookInfo + { + internal WebhookInfo(string name, ulong? channelId, string avatar) + { + Name = name; + ChannelId = channelId; + Avatar = avatar; + } + + public string Name { get; } + public ulong? ChannelId { get; } + public string Avatar { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs new file mode 100644 index 000000000..54da42a8b --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class WebhookUpdateAuditLogData : IAuditLogData + { + private WebhookUpdateAuditLogData(IWebhook webhook, WebhookInfo before, WebhookInfo after) + { + Webhook = webhook; + Before = before; + After = after; + } + + internal static WebhookUpdateAuditLogData Create(BaseDiscordClient discord, Model log, EntryModel entry) + { + var changes = entry.Changes; + + var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); + var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); + + var oldName = nameModel?.OldValue?.ToObject(); + var oldChannelId = channelIdModel?.OldValue?.ToObject(); + var oldAvatar = avatarHashModel?.OldValue?.ToObject(); + var before = new WebhookInfo(oldName, oldChannelId, oldAvatar); + + var newName = nameModel?.NewValue?.ToObject(); + var newChannelId = channelIdModel?.NewValue?.ToObject(); + var newAvatar = avatarHashModel?.NewValue?.ToObject(); + var after = new WebhookInfo(newName, newChannelId, newAvatar); + + var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); + var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo); + + return new WebhookUpdateAuditLogData(webhook, before, after); + } + + //Again, the *current* data + public IWebhook Webhook { get; } + + //And the *audit log* data + public WebhookInfo Before { get; } + public WebhookInfo After { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs new file mode 100644 index 000000000..9e30a5014 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs @@ -0,0 +1,38 @@ +using System.Linq; + +using Model = Discord.API.AuditLog; +using EntryModel = Discord.API.AuditLogEntry; + +namespace Discord.Rest +{ + public class RestAuditLogEntry : RestEntity, IAuditLogEntry + { + private RestAuditLogEntry(BaseDiscordClient discord, Model fullLog, EntryModel model, IUser user) + : base(discord, model.Id) + { + Action = model.Action; + Data = AuditLogHelper.CreateData(discord, fullLog, model); + User = user; + Reason = model.Reason; + } + + internal static RestAuditLogEntry Create(BaseDiscordClient discord, Model fullLog, EntryModel model) + { + var userInfo = fullLog.Users.FirstOrDefault(x => x.Id == model.UserId); + IUser user = null; + if (userInfo != null) + user = RestUser.Create(discord, userInfo); + + return new RestAuditLogEntry(discord, fullLog, model, user); + } + + /// + public ActionType Action { get; } + /// + public IAuditLogData Data { get; } + /// + public IUser User { get; } + /// + public string Reason { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index e3fd1a8c3..6ccb02f93 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -268,6 +268,35 @@ namespace Discord.Rest return model.Pruned; } + // Audit logs + public static IAsyncEnumerable> GetAuditLogsAsync(IGuild guild, BaseDiscordClient client, + ulong? from, int? limit, RequestOptions options) + { + return new PagedAsyncEnumerable( + DiscordConfig.MaxAuditLogEntriesPerBatch, + async (info, ct) => + { + var args = new GetAuditLogsParams + { + Limit = info.PageSize + }; + if (info.Position != null) + args.BeforeEntryId = info.Position.Value; + var model = await client.ApiClient.GetAuditLogsAsync(guild.Id, args, options); + return model.Entries.Select((x) => RestAuditLogEntry.Create(client, model, x)).ToImmutableArray(); + }, + nextPage: (info, lastPage) => + { + if (lastPage.Count != DiscordConfig.MaxAuditLogEntriesPerBatch) + return false; + info.Position = lastPage.Min(x => x.Id); + return true; + }, + start: from, + count: limit + ); + } + //Webhooks public static async Task GetWebhookAsync(IGuild guild, BaseDiscordClient client, ulong id, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index a7123930a..8d1937953 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -268,6 +268,10 @@ namespace Discord.Rest public Task PruneUsersAsync(int days = 30, bool simulate = false, RequestOptions options = null) => GuildHelper.PruneUsersAsync(this, Discord, days, simulate, options); + //Audit logs + public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null) + => GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options); + //Webhooks public Task GetWebhookAsync(ulong id, RequestOptions options = null) => GuildHelper.GetWebhookAsync(this, Discord, id, options); @@ -429,6 +433,14 @@ namespace Discord.Rest } Task IGuild.DownloadUsersAsync() { throw new NotSupportedException(); } + async Task> IGuild.GetAuditLogAsync(int limit, CacheMode cacheMode, RequestOptions options) + { + if (cacheMode == CacheMode.AllowDownload) + return (await GetAuditLogsAsync(limit, options).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); + else + return ImmutableArray.Create(); + } + async Task IGuild.GetWebhookAsync(ulong id, RequestOptions options) => await GetWebhookAsync(id, options); async Task> IGuild.GetWebhooksAsync(RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 3dbd1160c..e332fcd9a 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -438,6 +438,10 @@ namespace Discord.WebSocket _downloaderPromise.TrySetResultAsync(true); } + //Audit logs + public IAsyncEnumerable> GetAuditLogsAsync(int limit, RequestOptions options = null) + => GuildHelper.GetAuditLogsAsync(this, Discord, null, limit, options); + //Webhooks public Task GetWebhookAsync(ulong id, RequestOptions options = null) => GuildHelper.GetWebhookAsync(this, Discord, id, options); @@ -703,6 +707,14 @@ namespace Discord.WebSocket Task IGuild.GetOwnerAsync(CacheMode mode, RequestOptions options) => Task.FromResult(Owner); + async Task> IGuild.GetAuditLogAsync(int limit, CacheMode cacheMode, RequestOptions options) + { + if (cacheMode == CacheMode.AllowDownload) + return (await GetAuditLogsAsync(limit, options).FlattenAsync().ConfigureAwait(false)).ToImmutableArray(); + else + return ImmutableArray.Create(); + } + async Task IGuild.GetWebhookAsync(ulong id, RequestOptions options) => await GetWebhookAsync(id, options); async Task> IGuild.GetWebhooksAsync(RequestOptions options) From 32fc2df21b1840fd913ccce80c0686e8e853b43d Mon Sep 17 00:00:00 2001 From: Alex Gravely Date: Sat, 12 May 2018 20:47:44 -0400 Subject: [PATCH 18/49] Remove unused field in EmbedFieldBuilder. (#1018) --- src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs index 62834ebf3..04f4f6884 100644 --- a/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs +++ b/src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs @@ -231,7 +231,6 @@ namespace Discord { private string _name; private string _value; - private EmbedField _field; public const int MaxFieldNameLength = 256; public const int MaxFieldValueLength = 1024; From e764dafe083bf5db62091ac1f6b3d438eee38882 Mon Sep 17 00:00:00 2001 From: Quahu Date: Sun, 13 May 2018 15:34:40 +0200 Subject: [PATCH 19/49] Add ViewChannel to Voice channel permissions (#1059) Previously, Voice channels did not have ViewChannel in their "all" permissions --- src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index ef10ee106..038b18e2e 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -12,7 +12,7 @@ namespace Discord /// Gets a ChannelPermissions that grants all permissions for text channels. public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); /// Gets a ChannelPermissions that grants all permissions for voice channels. - public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000000000_010001); + public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000010000_010001); /// Gets a ChannelPermissions that grants all permissions for category channels. public static readonly ChannelPermissions Category = new ChannelPermissions(0b01100_1111110_1111111110001_010001); /// Gets a ChannelPermissions that grants all permissions for direct message channels. From 79811d0e618c011e6cca542811031d824650dae5 Mon Sep 17 00:00:00 2001 From: HelpfulStranger999 Date: Thu, 24 May 2018 17:35:38 -0500 Subject: [PATCH 20/49] Paginate reactions - solved #1007 (#1022) * Cleaned up and refactored slightly * Resolves #971 * Adds support for default avatars and resolves #971 * Amendment * Final amendment * Paginating reactions * Amendments based on feedback * Further amendment based on review * Final(?) amendment * Removes default limit and after user id * Removes fromUserId; cleans up model creation; replaces function with individual parameters --- src/Discord.Net.Core/DiscordConfig.cs | 3 +- .../Entities/Messages/IUserMessage.cs | 4 +- .../API/Rest/GetReactionUsersParams.cs | 2 +- src/Discord.Net.Rest/DiscordRestApiClient.cs | 4 +- .../Entities/Messages/MessageHelper.cs | 37 ++++++++++++++++--- .../Entities/Messages/RestUserMessage.cs | 4 +- .../Entities/Messages/SocketUserMessage.cs | 4 +- 7 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 1db3271e5..5cdf6a77e 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -1,4 +1,4 @@ -using System.Reflection; +using System.Reflection; namespace Discord { @@ -20,6 +20,7 @@ namespace Discord public const int MaxMessagesPerBatch = 100; public const int MaxUsersPerBatch = 1000; public const int MaxGuildsPerBatch = 100; + public const int MaxUserReactionsPerBatch = 100; public const int MaxAuditLogEntriesPerBatch = 100; /// Gets or sets how a request should act in the case of an error, by default. diff --git a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs index 52df187f8..36ee725ff 100644 --- a/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs +++ b/src/Discord.Net.Core/Entities/Messages/IUserMessage.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -23,7 +23,7 @@ namespace Discord /// Removes all reactions from this message. Task RemoveAllReactionsAsync(RequestOptions options = null); /// Gets all users that reacted to a message with a given emote - Task> GetReactionUsersAsync(IEmote emoji, int limit = 100, ulong? afterUserId = null, RequestOptions options = null); + IAsyncEnumerable> GetReactionUsersAsync(IEmote emoji, int limit, 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/API/Rest/GetReactionUsersParams.cs b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs index d70da5632..a0967bb75 100644 --- a/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs +++ b/src/Discord.Net.Rest/API/Rest/GetReactionUsersParams.cs @@ -1,4 +1,4 @@ -namespace Discord.API.Rest +namespace Discord.API.Rest { internal class GetReactionUsersParams { diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 8b641fb6a..85e04f962 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -618,11 +618,11 @@ namespace Discord.API 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.AtMost(args.Limit, DiscordConfig.MaxUserReactionsPerBatch, nameof(args.Limit)); Preconditions.GreaterThan(args.AfterUserId, 0, nameof(args.AfterUserId)); options = RequestOptions.CreateOrClone(options); - int limit = args.Limit.GetValueOrDefault(int.MaxValue); + int limit = args.Limit.GetValueOrDefault(DiscordConfig.MaxUserReactionsPerBatch); ulong afterUserId = args.AfterUserId.GetValueOrDefault(0); var ids = new BucketIds(channelId: channelId); diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index 8ae41cc37..f7ce3ded0 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -46,13 +46,38 @@ namespace Discord.Rest await client.ApiClient.RemoveAllReactionsAsync(msg.Channel.Id, msg.Id, options); } - public static async Task> GetReactionUsersAsync(IMessage msg, IEmote emote, - Action func, BaseDiscordClient client, RequestOptions options) + public static IAsyncEnumerable> GetReactionUsersAsync(IMessage msg, IEmote emote, + int? limit, BaseDiscordClient client, RequestOptions options) { - var args = new GetReactionUsersParams(); - func(args); - string emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name); - return (await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false)).Select(u => RestUser.Create(client, u)).ToImmutableArray(); + Preconditions.NotNull(emote, nameof(emote)); + var emoji = (emote is Emote e ? $"{e.Name}:{e.Id}" : emote.Name); + + return new PagedAsyncEnumerable( + DiscordConfig.MaxUserReactionsPerBatch, + async (info, ct) => + { + var args = new GetReactionUsersParams + { + Limit = info.PageSize + }; + + if (info.Position != null) + args.AfterUserId = info.Position.Value; + + var models = await client.ApiClient.GetReactionUsersAsync(msg.Channel.Id, msg.Id, emoji, args, options).ConfigureAwait(false); + return models.Select(x => RestUser.Create(client, x)).ToImmutableArray(); + }, + nextPage: (info, lastPage) => + { + if (lastPage.Count != DiscordConfig.MaxUsersPerBatch) + return false; + + info.Position = lastPage.Max(x => x.Id); + return true; + }, + count: limit + ); + } public static async Task PinAsync(IMessage msg, BaseDiscordClient client, diff --git a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs index 0d1f3be2b..7354cc4af 100644 --- a/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs +++ b/src/Discord.Net.Rest/Entities/Messages/RestUserMessage.cs @@ -136,8 +136,8 @@ namespace Discord.Rest => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); public Task RemoveAllReactionsAsync(RequestOptions options = null) => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); - public Task> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create(); }, Discord, options); + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); public Task PinAsync(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs index 5489ad2bb..f8c15a986 100644 --- a/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs +++ b/src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs @@ -130,8 +130,8 @@ namespace Discord.WebSocket => MessageHelper.RemoveReactionAsync(this, user, emote, Discord, options); public Task RemoveAllReactionsAsync(RequestOptions options = null) => MessageHelper.RemoveAllReactionsAsync(this, Discord, options); - public Task> GetReactionUsersAsync(IEmote emote, int limit = 100, ulong? afterUserId = null, RequestOptions options = null) - => MessageHelper.GetReactionUsersAsync(this, emote, x => { x.Limit = limit; x.AfterUserId = afterUserId ?? Optional.Create(); }, Discord, options); + public IAsyncEnumerable> GetReactionUsersAsync(IEmote emote, int limit, RequestOptions options = null) + => MessageHelper.GetReactionUsersAsync(this, emote, limit, Discord, options); public Task PinAsync(RequestOptions options = null) => MessageHelper.PinAsync(this, Discord, options); From 0ba8b063ad98144232606f422574f9741fd95b7a Mon Sep 17 00:00:00 2001 From: HelpfulStranger999 Date: Thu, 24 May 2018 19:23:44 -0400 Subject: [PATCH 21/49] Makes text parameter of sending messages optional (#1042) commit 114e5b431b26669bcdaac9f84792a216ad67186f Author: HelpfulStranger999 Date: Sat Apr 28 19:08:35 2018 -0500 Fixes lack of default value for tts commit 1fd8c70c5346ff0c4fdb0093c0fc7fb4b3c8db2c Author: HelpfulStranger999 Date: Sat Apr 28 15:21:11 2018 -0500 Makes text parameter of sending messages optional --- src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs | 2 +- src/Discord.Net.Core/Extensions/UserExtensions.cs | 2 +- .../Entities/Channels/IRestMessageChannel.cs | 2 +- src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs | 2 +- src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs | 2 +- src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs | 2 +- .../Entities/Channels/RpcVirtualMessageChannel.cs | 6 +++--- .../Entities/Channels/ISocketMessageChannel.cs | 2 +- .../Entities/Channels/SocketDMChannel.cs | 2 +- .../Entities/Channels/SocketGroupChannel.cs | 2 +- .../Entities/Channels/SocketTextChannel.cs | 2 +- src/Discord.Net.Webhook/DiscordWebhookClient.cs | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 5a6e5df59..099b9da43 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, Embed embed = null, RequestOptions options = null); + Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if FILESYSTEM /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Extensions/UserExtensions.cs b/src/Discord.Net.Core/Extensions/UserExtensions.cs index d3e968e39..3296d00fd 100644 --- a/src/Discord.Net.Core/Extensions/UserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/UserExtensions.cs @@ -9,7 +9,7 @@ namespace Discord /// Sends a message to the user via DM. /// public static async Task SendMessageAsync(this IUser user, - string text, + string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index 8ab6e9819..b6f891f40 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, Embed embed = null, RequestOptions options = null); + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if FILESYSTEM /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 08acdf32b..f65ce79de 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, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index a1868573e..af9d80d84 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, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 600b197d6..8a33ebd05 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -58,7 +58,7 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs index 3b6a68193..14cbc33f1 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs @@ -33,13 +33,13 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM - public Task SendFileAsync(string filePath, string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); #endif - public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed = null, RequestOptions options = null) + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); public Task TriggerTypingAsync(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index 026bd8378..971dbe8f1 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, Embed embed = null, RequestOptions options = null); + new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); #if FILESYSTEM /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 451240e66..764431ae1 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -67,7 +67,7 @@ namespace Discord.WebSocket public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index d46c5fc59..1d68109ee 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, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index ec7615b55..f776c9956 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -75,7 +75,7 @@ namespace Discord.WebSocket public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); - public Task SendMessageAsync(string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 59cc8f3e7..2dea1c1d1 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -63,7 +63,7 @@ namespace Discord.Webhook => new API.DiscordRestApiClient(config.RestClientProvider, DiscordRestConfig.UserAgent); /// Sends a message using to the channel for this webhook. Returns the ID of the created message. - public Task SendMessageAsync(string text, bool isTTS = false, IEnumerable embeds = null, + public Task SendMessageAsync(string text = null, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, options); From bf5275e071b78a25fd4501a930e69c5dc8cd3b19 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Thu, 24 May 2018 19:36:00 -0400 Subject: [PATCH 22/49] Add ability to specify parameters on channel creation (#1020) commit 07bca5b31a3580d55278878eabb56a82973f8c8f Author: Joe4evr Date: Fri Apr 6 09:44:50 2018 +0200 Add ability to specify parameters on channel creation --- .../Entities/Guilds/IGuild.cs | 4 ++-- .../API/Rest/CreateGuildChannelParams.cs | 13 +++++++++- .../Entities/Guilds/GuildHelper.cs | 24 +++++++++++++++---- .../Entities/Guilds/RestGuild.cs | 16 ++++++------- .../Entities/Guilds/SocketGuild.cs | 16 ++++++------- 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index 19d2ee81c..f6e13e0e1 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -109,9 +109,9 @@ namespace Discord 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); + Task CreateTextChannelAsync(string name, RequestOptions options = null, Action func = null); /// Creates a new voice channel. - Task CreateVoiceChannelAsync(string name, RequestOptions options = null); + Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null); /// Creates a new channel category. Task CreateCategoryAsync(string name, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs index bae677148..05cdf4b8a 100644 --- a/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs +++ b/src/Discord.Net.Rest/API/Rest/CreateGuildChannelParams.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Rest @@ -10,9 +10,20 @@ namespace Discord.API.Rest public string Name { get; } [JsonProperty("type")] public ChannelType Type { get; } + [JsonProperty("parent_id")] + public Optional CategoryId { get; set; } + //Text channels + [JsonProperty("topic")] + public Optional Topic { get; set; } + [JsonProperty("nsfw")] + public Optional IsNsfw { get; set; } + + //Voice channels [JsonProperty("bitrate")] public Optional Bitrate { get; set; } + [JsonProperty("user_limit")] + public Optional UserLimit { get; set; } public CreateGuildChannelParams(string name, ChannelType type) { diff --git a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs index 6ccb02f93..c76b41a91 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/GuildHelper.cs @@ -145,20 +145,36 @@ namespace Discord.Rest return models.Select(x => RestGuildChannel.Create(client, guild, x)).ToImmutableArray(); } public static async Task CreateTextChannelAsync(IGuild guild, BaseDiscordClient client, - string name, RequestOptions options) + string name, RequestOptions options, Action func = null) { if (name == null) throw new ArgumentNullException(nameof(name)); - var args = new CreateGuildChannelParams(name, ChannelType.Text); + var props = new TextChannelProperties(); + func?.Invoke(props); + + var args = new CreateGuildChannelParams(name, ChannelType.Text) + { + CategoryId = props.CategoryId, + Topic = props.Topic, + IsNsfw = props.IsNsfw + }; var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); return RestTextChannel.Create(client, guild, model); } public static async Task CreateVoiceChannelAsync(IGuild guild, BaseDiscordClient client, - string name, RequestOptions options) + string name, RequestOptions options, Action func = null) { if (name == null) throw new ArgumentNullException(nameof(name)); - var args = new CreateGuildChannelParams(name, ChannelType.Voice); + var props = new VoiceChannelProperties(); + func?.Invoke(props); + + var args = new CreateGuildChannelParams(name, ChannelType.Voice) + { + CategoryId = props.CategoryId, + Bitrate = props.Bitrate, + UserLimit = props.UserLimit + }; var model = await client.ApiClient.CreateGuildChannelAsync(guild.Id, args, options).ConfigureAwait(false); return RestVoiceChannel.Create(client, guild, model); } diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 8d1937953..91b548be4 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -222,10 +222,10 @@ namespace Discord.Rest } return null; } - public Task CreateTextChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateTextChannelAsync(this, Discord, name, options); - public Task CreateVoiceChannelAsync(string name, RequestOptions options = null) - => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); + public Task CreateTextChannelAsync(string name, RequestOptions options = null, Action func = null) + => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); + public Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null) + => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -383,10 +383,10 @@ namespace Discord.Rest 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) - => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options, Action func) + => await CreateTextChannelAsync(name, options, func).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options, Action func) + => await CreateVoiceChannelAsync(name, options, func).ConfigureAwait(false); async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index e332fcd9a..a6ef1925b 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -315,10 +315,10 @@ namespace Discord.WebSocket => 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) - => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options); + public Task CreateTextChannelAsync(string name, RequestOptions options = null, Action func = null) + => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); + public Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null) + => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -678,10 +678,10 @@ namespace Discord.WebSocket => Task.FromResult(EmbedChannel); Task IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) => Task.FromResult(SystemChannel); - async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options) - => await CreateTextChannelAsync(name, options).ConfigureAwait(false); - async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options) - => await CreateVoiceChannelAsync(name, options).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options, Action func) + => await CreateTextChannelAsync(name, options, func).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options, Action func) + => await CreateVoiceChannelAsync(name, options, func).ConfigureAwait(false); async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); From 5023357a60bd5ae454b35426ddd279fb3284b7e8 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Thu, 24 May 2018 19:36:33 -0400 Subject: [PATCH 23/49] codefix #1020: RequestOptions should always be sorted last --- src/Discord.Net.Core/Entities/Guilds/IGuild.cs | 4 ++-- src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs | 12 ++++++------ .../Entities/Guilds/SocketGuild.cs | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs index f6e13e0e1..4c1c274d0 100644 --- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs +++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs @@ -109,9 +109,9 @@ namespace Discord 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, Action func = null); + Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null); /// Creates a new voice channel. - Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null); + Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null); /// Creates a new channel category. Task CreateCategoryAsync(string name, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs index 91b548be4..357e22c85 100644 --- a/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs +++ b/src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs @@ -222,9 +222,9 @@ namespace Discord.Rest } return null; } - public Task CreateTextChannelAsync(string name, RequestOptions options = null, Action func = null) + public Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); - public Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null) + public Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -383,10 +383,10 @@ namespace Discord.Rest else return null; } - async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options, Action func) - => await CreateTextChannelAsync(name, options, func).ConfigureAwait(false); - async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options, Action func) - => await CreateVoiceChannelAsync(name, options, func).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, Action func, RequestOptions options) + => await CreateTextChannelAsync(name, func, options).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, Action func, RequestOptions options) + => await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false); async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index a6ef1925b..14263f0af 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -315,9 +315,9 @@ namespace Discord.WebSocket => GetChannel(id) as SocketTextChannel; public SocketVoiceChannel GetVoiceChannel(ulong id) => GetChannel(id) as SocketVoiceChannel; - public Task CreateTextChannelAsync(string name, RequestOptions options = null, Action func = null) + public Task CreateTextChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateTextChannelAsync(this, Discord, name, options, func); - public Task CreateVoiceChannelAsync(string name, RequestOptions options = null, Action func = null) + public Task CreateVoiceChannelAsync(string name, Action func = null, RequestOptions options = null) => GuildHelper.CreateVoiceChannelAsync(this, Discord, name, options, func); public Task CreateCategoryChannelAsync(string name, RequestOptions options = null) => GuildHelper.CreateCategoryChannelAsync(this, Discord, name, options); @@ -678,10 +678,10 @@ namespace Discord.WebSocket => Task.FromResult(EmbedChannel); Task IGuild.GetSystemChannelAsync(CacheMode mode, RequestOptions options) => Task.FromResult(SystemChannel); - async Task IGuild.CreateTextChannelAsync(string name, RequestOptions options, Action func) - => await CreateTextChannelAsync(name, options, func).ConfigureAwait(false); - async Task IGuild.CreateVoiceChannelAsync(string name, RequestOptions options, Action func) - => await CreateVoiceChannelAsync(name, options, func).ConfigureAwait(false); + async Task IGuild.CreateTextChannelAsync(string name, Action func, RequestOptions options) + => await CreateTextChannelAsync(name, func, options).ConfigureAwait(false); + async Task IGuild.CreateVoiceChannelAsync(string name, Action func, RequestOptions options) + => await CreateVoiceChannelAsync(name, func, options).ConfigureAwait(false); async Task IGuild.CreateCategoryAsync(string name, RequestOptions options) => await CreateCategoryChannelAsync(name, options).ConfigureAwait(false); From c275e575289073fe42ead725de97b97301c7dc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?o=20Acid=20Chicken=20=28=E7=A1=AB=E7=A1=AB=E2=96=92=7EE?= =?UTF-8?q?=E2=96=92=E2=96=92=E2=96=92=7EO=29?= Date: Thu, 24 May 2018 19:52:25 -0400 Subject: [PATCH 24/49] Add support casting System.Drawing.Color to Discord.Color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit fa3303426766a59e7aa4d67e8b50826cfe7204ec Author: Acid Chicken (硫酸鶏) Date: Sun Apr 29 13:50:56 2018 +0900 Use built-in symbol refs: https://github.com/RogueException/Discord.Net/pull/1043#issuecomment-385223999 commit 27ea82668587960925b73f97c924c9d39ee71f7b Author: Acid Chicken (硫酸鶏) Date: Sun Apr 29 11:29:31 2018 +0900 Add support casting System.Drawing.Color to Discord.Color commit 1ab9de24978ff24a018767f80dc95ba19b616988 Merge: f5bb99c7 a4d1e2bc Author: Acid Chicken (硫酸鶏) Date: Sun Apr 29 10:16:46 2018 +0900 Merge remote-tracking branch 'upstream/dev' into dev commit f5bb99c77d0fecec21ad769778d17144c047d8b1 Merge: 3be8e40d b8b59d97 Author: Acid Chicken (硫酸鶏) Date: Tue Apr 10 11:07:36 2018 +0900 Merge remote-tracking branch 'upstream/dev' into dev commit 3be8e40d3814fe8455af475b8641ee07a8f368d6 Merge: c692306f 9d77a3cd Author: Acid Chicken (硫酸鶏) Date: Sun Jan 7 15:05:31 2018 +0900 Merge remote-tracking branch 'upstream/dev' into dev commit c692306fcc1e86ab92dd10683d3719f16c02a249 Author: Acid Chicken (硫酸鶏) Date: Sat Nov 11 10:49:00 2017 +0900 Add target of the internal fields commit 2d08f9a655b4949c1177f778d0f499047484a537 Author: Acid Chicken (硫酸鶏) Date: Sat Nov 11 10:17:42 2017 +0900 Add some more extension commit 4f19b835ffe8c64a93a9b4659e60b03ac797760f Author: Acid Chicken (硫酸鶏) Date: Sat Nov 11 01:19:11 2017 +0900 Add naming rules commit af756cd9feb630baadbf6025cbb079cd9e1f45cb Author: Acid Chicken (硫酸鶏) Date: Sat Nov 11 00:35:30 2017 +0900 Add basic .NET style rules commit 503ece558b4f07bd8008157d3aeb6a4e7100d349 Author: Acid Chicken (硫酸鶏) Date: Fri Nov 10 22:36:52 2017 +0900 Add EditorConfig --- src/Discord.Net.Core/Discord.Net.Core.csproj | 4 ++-- src/Discord.Net.Core/Entities/Roles/Color.cs | 12 +++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 7565fa178..321803114 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -4,8 +4,8 @@ Discord.Net.Core Discord The core components for the Discord.Net library. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net45;netstandard1.1;netstandard1.3;netstandard2.0 + netstandard1.1;netstandard1.3;netstandard2.0 diff --git a/src/Discord.Net.Core/Entities/Roles/Color.cs b/src/Discord.Net.Core/Entities/Roles/Color.cs index 89e76df6d..0bb04d339 100644 --- a/src/Discord.Net.Core/Entities/Roles/Color.cs +++ b/src/Discord.Net.Core/Entities/Roles/Color.cs @@ -1,5 +1,8 @@ using System; using System.Diagnostics; +#if NETSTANDARD2_0 || NET45 +using StandardColor = System.Drawing.Color; +#endif namespace Discord { @@ -96,7 +99,14 @@ namespace Discord ((uint)(g * 255.0f) << 8) | (uint)(b * 255.0f); } - + +#if NETSTANDARD2_0 || NET45 + public static implicit operator StandardColor(Color color) => + StandardColor.FromArgb((int)color.RawValue); + public static explicit operator Color(StandardColor color) => + new Color((uint)color.ToArgb() << 8 >> 8); +#endif + public override string ToString() => $"#{Convert.ToString(RawValue, 16)}"; private string DebuggerDisplay => From b52af7ae7caaddd79101aeb9f5af2ce482890bc2 Mon Sep 17 00:00:00 2001 From: Joe4evr Date: Fri, 25 May 2018 01:59:32 +0200 Subject: [PATCH 25/49] Add a dedicated TimeSpan reader so it doesn't suck (#1005) * Add a dedicated TimeSpan reader so it doesn't suck * Pass input as lower case --- src/Discord.Net.Commands/CommandService.cs | 4 +++ src/Discord.Net.Commands/PrimitiveParsers.cs | 4 +-- .../Readers/TimeSpanTypeReader.cs | 35 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index c996f7334..b20c231c3 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -65,6 +65,10 @@ namespace Discord.Commands _defaultTypeReaders[typeof(Nullable<>).MakeGenericType(type)] = NullableTypeReader.Create(type, _defaultTypeReaders[type]); } + var tsreader = new TimeSpanTypeReader(); + _defaultTypeReaders[typeof(TimeSpan)] = tsreader; + _defaultTypeReaders[typeof(TimeSpan?)] = NullableTypeReader.Create(typeof(TimeSpan), tsreader); + _defaultTypeReaders[typeof(string)] = new PrimitiveTypeReader((string x, out string y) => { y = x; return true; }, 0); diff --git a/src/Discord.Net.Commands/PrimitiveParsers.cs b/src/Discord.Net.Commands/PrimitiveParsers.cs index 6a54ba402..bf0622c28 100644 --- a/src/Discord.Net.Commands/PrimitiveParsers.cs +++ b/src/Discord.Net.Commands/PrimitiveParsers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -29,7 +29,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(TimeSpan)] = (TryParseDelegate)TimeSpan.TryParse; parserBuilder[typeof(char)] = (TryParseDelegate)char.TryParse; return parserBuilder.ToImmutable(); } diff --git a/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs b/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs new file mode 100644 index 000000000..31ab9d821 --- /dev/null +++ b/src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs @@ -0,0 +1,35 @@ +using System; +using System.Globalization; +using System.Threading.Tasks; + +namespace Discord.Commands +{ + internal class TimeSpanTypeReader : TypeReader + { + private static readonly string[] _formats = new[] + { + "%d'd'%h'h'%m'm'%s's'", //4d3h2m1s + "%d'd'%h'h'%m'm'", //4d3h2m + "%d'd'%h'h'%s's'", //4d3h 1s + "%d'd'%h'h'", //4d3h + "%d'd'%m'm'%s's'", //4d 2m1s + "%d'd'%m'm'", //4d 2m + "%d'd'%s's'", //4d 1s + "%d'd'", //4d + "%h'h'%m'm'%s's'", // 3h2m1s + "%h'h'%m'm'", // 3h2m + "%h'h'%s's'", // 3h 1s + "%h'h'", // 3h + "%m'm'%s's'", // 2m1s + "%m'm'", // 2m + "%s's'", // 1s + }; + + public override Task ReadAsync(ICommandContext context, string input, IServiceProvider services) + { + return (TimeSpan.TryParseExact(input.ToLowerInvariant(), _formats, CultureInfo.InvariantCulture, out var timeSpan)) + ? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan)) + : Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan")); + } + } +} From cee71ef35a450802ef35090d9eaf167ca39306e8 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Thu, 24 May 2018 17:07:37 -0700 Subject: [PATCH 26/49] Add support for parsing multiple types of quotation marks in commands, Fix #942 (#943) * Add ability to support different types of quotation marks * Added normal quotation mark to list of aliases, removed single quote mark * clean up leftover changes from testing * change quotation mark parsing to use a map of matching pairs * remove commented out code * Fix conventions of the command parser utility functions * change storage type of alias dictionary to be IReadOnlyDictionary * revert type of CommandServiceConfig QuotationMarkAliasMap to Dictionary * minor formatting changes to CommandParser * remove unnecessary whitespace * Move aliases outside of CommandInfo class * copy IReadOnlyDictionary to ImmutableDictionary * minor syntax changes in CommandServiceConfig * add newline before namespace for consistency * newline formatting tweak * simplification of GetMatch method for CommandParser * add more quote unicode punctuation pairs * add check for null value when building ImmutableDictionary * Move default alias map into a separate source file * Ensure that the collection passed into command service is not null --- src/Discord.Net.Commands/CommandParser.cs | 34 +++++-- src/Discord.Net.Commands/CommandService.cs | 5 +- .../CommandServiceConfig.cs | 5 + src/Discord.Net.Commands/Info/CommandInfo.cs | 5 +- .../Utilities/QuotationAliasUtils.cs | 95 +++++++++++++++++++ 5 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index 64daf841e..9ce4e1469 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using System.Threading.Tasks; @@ -13,8 +14,7 @@ namespace Discord.Commands Parameter, QuotedParameter } - - public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, IServiceProvider services, string input, int startPos) + public static async Task ParseArgsAsync(CommandInfo command, ICommandContext context, bool ignoreExtraArgs, IServiceProvider services, string input, int startPos, IReadOnlyDictionary aliasMap) { ParameterInfo curParam = null; StringBuilder argBuilder = new StringBuilder(input.Length); @@ -24,7 +24,27 @@ namespace Discord.Commands var argList = ImmutableArray.CreateBuilder(); var paramList = ImmutableArray.CreateBuilder(); bool isEscaping = false; - char c; + char c, matchQuote = '\0'; + + // local helper functions + bool IsOpenQuote(IReadOnlyDictionary dict, char ch) + { + // return if the key is contained in the dictionary if it is populated + if (dict.Count != 0) + return dict.ContainsKey(ch); + // or otherwise if it is the default double quote + return c == '\"'; + } + + char GetMatch(IReadOnlyDictionary dict, char ch) + { + // get the corresponding value for the key, if it exists + // and if the dictionary is populated + if (dict.Count != 0 && dict.TryGetValue(c, out var value)) + return value; + // or get the default pair of the default double quote + return '\"'; + } for (int curPos = startPos; curPos <= endPos; curPos++) { @@ -74,9 +94,11 @@ namespace Discord.Commands argBuilder.Append(c); continue; } - if (c == '\"') + + if (IsOpenQuote(aliasMap, c)) { curPart = ParserPart.QuotedParameter; + matchQuote = GetMatch(aliasMap, c); continue; } curPart = ParserPart.Parameter; @@ -97,7 +119,7 @@ namespace Discord.Commands } else if (curPart == ParserPart.QuotedParameter) { - if (c == '\"') + if (c == matchQuote) { argString = argBuilder.ToString(); //Remove quotes lastArgEndPos = curPos + 1; diff --git a/src/Discord.Net.Commands/CommandService.cs b/src/Discord.Net.Commands/CommandService.cs index b20c231c3..f75653ccf 100644 --- a/src/Discord.Net.Commands/CommandService.cs +++ b/src/Discord.Net.Commands/CommandService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -32,6 +32,7 @@ namespace Discord.Commands internal readonly RunMode _defaultRunMode; internal readonly Logger _cmdLogger; internal readonly LogManager _logManager; + internal readonly IReadOnlyDictionary _quotationMarkAliasMap; public IEnumerable Modules => _moduleDefs.Select(x => x); public IEnumerable Commands => _moduleDefs.SelectMany(x => x.Commands); @@ -45,6 +46,7 @@ namespace Discord.Commands _ignoreExtraArgs = config.IgnoreExtraArgs; _separatorChar = config.SeparatorChar; _defaultRunMode = config.DefaultRunMode; + _quotationMarkAliasMap = (config.QuotationMarkAliasMap ?? new Dictionary()).ToImmutableDictionary(); if (_defaultRunMode == RunMode.Default) throw new InvalidOperationException("The default run mode cannot be set to Default."); @@ -337,7 +339,6 @@ namespace Discord.Commands public async Task ExecuteAsync(ICommandContext context, string input, IServiceProvider services, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) { services = services ?? EmptyServiceProvider.Instance; - var searchResult = Search(context, input); if (!searchResult.IsSuccess) return searchResult; diff --git a/src/Discord.Net.Commands/CommandServiceConfig.cs b/src/Discord.Net.Commands/CommandServiceConfig.cs index f5cd14fef..00091634d 100644 --- a/src/Discord.Net.Commands/CommandServiceConfig.cs +++ b/src/Discord.Net.Commands/CommandServiceConfig.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Discord.Commands { @@ -18,6 +19,10 @@ namespace Discord.Commands /// Determines whether RunMode.Sync commands should push exceptions up to the caller. public bool ThrowOnError { get; set; } = true; + /// Collection of aliases that can wrap strings for command parsing. + /// represents the opening quotation mark and the value is the corresponding closing mark. + public Dictionary QuotationMarkAliasMap { get; set; } = QuotationAliasUtils.GetDefaultAliasMap; + /// Determines whether extra parameters should be ignored. public bool IgnoreExtraArgs { get; set; } = false; } diff --git a/src/Discord.Net.Commands/Info/CommandInfo.cs b/src/Discord.Net.Commands/Info/CommandInfo.cs index 1a9cf69c5..df0bb297b 100644 --- a/src/Discord.Net.Commands/Info/CommandInfo.cs +++ b/src/Discord.Net.Commands/Info/CommandInfo.cs @@ -1,4 +1,4 @@ -using Discord.Commands.Builders; +using Discord.Commands.Builders; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -121,7 +121,8 @@ namespace Discord.Commands return ParseResult.FromError(preconditionResult); string input = searchResult.Text.Substring(startIndex); - return await CommandParser.ParseArgsAsync(this, context, services, input, 0).ConfigureAwait(false); + + return await CommandParser.ParseArgsAsync(this, context, _commandService._ignoreExtraArgs, services, input, 0, _commandService._quotationMarkAliasMap).ConfigureAwait(false); } public Task ExecuteAsync(ICommandContext context, ParseResult parseResult, IServiceProvider services) diff --git a/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs new file mode 100644 index 000000000..15a08b9b3 --- /dev/null +++ b/src/Discord.Net.Commands/Utilities/QuotationAliasUtils.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Discord.Commands +{ + /// + /// Utility methods for generating matching pairs of unicode quotation marks for CommandServiceConfig + /// + internal static class QuotationAliasUtils + { + /// + /// Generates an IEnumerable of characters representing open-close pairs of + /// quotation punctuation. + /// + internal static Dictionary GetDefaultAliasMap + { + get + { + // Output of a gist provided by https://gist.github.com/ufcpp + // https://gist.github.com/ufcpp/5b2cf9a9bf7d0b8743714a0b88f7edc5 + // This was not used for the implementation because of incompatibility with netstandard1.1 + return new Dictionary { + {'\"', '\"' }, + {'«', '»' }, + {'‘', '’' }, + {'“', '”' }, + {'„', '‟' }, + {'‹', '›' }, + {'‚', '‛' }, + {'《', '》' }, + {'〈', '〉' }, + {'「', '」' }, + {'『', '』' }, + {'〝', '〞' }, + {'﹁', '﹂' }, + {'﹃', '﹄' }, + {'"', '"' }, + {''', ''' }, + {'「', '」' }, + {'(', ')' }, + {'༺', '༻' }, + {'༼', '༽' }, + {'᚛', '᚜' }, + {'⁅', '⁆' }, + {'⌈', '⌉' }, + {'⌊', '⌋' }, + {'❨', '❩' }, + {'❪', '❫' }, + {'❬', '❭' }, + {'❮', '❯' }, + {'❰', '❱' }, + {'❲', '❳' }, + {'❴', '❵' }, + {'⟅', '⟆' }, + {'⟦', '⟧' }, + {'⟨', '⟩' }, + {'⟪', '⟫' }, + {'⟬', '⟭' }, + {'⟮', '⟯' }, + {'⦃', '⦄' }, + {'⦅', '⦆' }, + {'⦇', '⦈' }, + {'⦉', '⦊' }, + {'⦋', '⦌' }, + {'⦍', '⦎' }, + {'⦏', '⦐' }, + {'⦑', '⦒' }, + {'⦓', '⦔' }, + {'⦕', '⦖' }, + {'⦗', '⦘' }, + {'⧘', '⧙' }, + {'⧚', '⧛' }, + {'⧼', '⧽' }, + {'⸂', '⸃' }, + {'⸄', '⸅' }, + {'⸉', '⸊' }, + {'⸌', '⸍' }, + {'⸜', '⸝' }, + {'⸠', '⸡' }, + {'⸢', '⸣' }, + {'⸤', '⸥' }, + {'⸦', '⸧' }, + {'⸨', '⸩' }, + {'【', '】'}, + {'〔', '〕' }, + {'〖', '〗' }, + {'〘', '〙' }, + {'〚', '〛' } + }; + } + } + } +} From bc6009ec7207e16aedecdc658dc59b8ac02c400e Mon Sep 17 00:00:00 2001 From: Still Hsu <341464@gmail.com> Date: Fri, 25 May 2018 08:08:51 +0800 Subject: [PATCH 27/49] Implement IMessageChannel#DeleteMessageAsync (#996) * Implement DeleteMessageAsync * Refer to MessageHelper instead of duplicating call * Fix refactor error --- src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs | 5 +++++ src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 4 ++++ src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs | 5 +++++ src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs | 5 +++++ src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs | 5 +++++ .../Entities/Channels/RpcVirtualMessageChannel.cs | 5 +++++ src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs | 6 ++++-- .../Entities/Channels/SocketDMChannel.cs | 5 +++++ .../Entities/Channels/SocketGroupChannel.cs | 5 +++++ .../Entities/Channels/SocketTextChannel.cs | 5 +++++ 10 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 099b9da43..262b865ea 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -30,6 +30,11 @@ namespace Discord /// Gets a collection of pinned messages in this channel. Task> GetPinnedMessagesAsync(RequestOptions options = null); + /// Deletes a message based on the message ID in this channel. + Task DeleteMessageAsync(ulong messageId, RequestOptions options = null); + /// Deletes a message based on the provided message in this channel. + Task DeleteMessageAsync(IMessage message, RequestOptions options = null); + /// Broadcasts the "user is typing" message to all users in this channel, lasting 10 seconds. Task TriggerTypingAsync(RequestOptions options = null); /// Continuously broadcasts the "user is typing" message to all users in this channel until the returned object is disposed. diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 6784f7f6a..dbbabbd72 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -185,6 +185,10 @@ namespace Discord.Rest return RestUserMessage.Create(client, channel, client.CurrentUser, model); } + public static Task DeleteMessageAsync(IMessageChannel channel, ulong messageId, BaseDiscordClient client, + RequestOptions options) + => MessageHelper.DeleteAsync(channel.Id, messageId, client, options); + public static async Task DeleteMessagesAsync(ITextChannel channel, BaseDiscordClient client, IEnumerable messageIds, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index f65ce79de..21cd579e6 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -72,6 +72,11 @@ namespace Discord.Rest public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index af9d80d84..901016a6b 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -76,6 +76,11 @@ namespace Discord.Rest public Task> GetPinnedMessagesAsync(RequestOptions options = null) => ChannelHelper.GetPinnedMessagesAsync(this, Discord, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); #if FILESYSTEM diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 8a33ebd05..841aad666 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -67,6 +67,11 @@ namespace Discord.Rest public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task DeleteMessagesAsync(IEnumerable messages, RequestOptions options = null) => ChannelHelper.DeleteMessagesAsync(this, Discord, messages.Select(x => x.Id), options); public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs index 14cbc33f1..8f69388d4 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs @@ -42,6 +42,11 @@ namespace Discord.Rest public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) diff --git a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs index f7ce3ded0..3dc3e74e9 100644 --- a/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs +++ b/src/Discord.Net.Rest/Entities/Messages/MessageHelper.cs @@ -25,10 +25,12 @@ namespace Discord.Rest }; return await client.ApiClient.ModifyMessageAsync(msg.Channel.Id, msg.Id, apiArgs, options).ConfigureAwait(false); } - public static async Task DeleteAsync(IMessage msg, BaseDiscordClient client, + public static Task DeleteAsync(IMessage msg, BaseDiscordClient client, RequestOptions options) + => DeleteAsync(msg.Channel.Id, msg.Id, client, options); + public static async Task DeleteAsync(ulong channelId, ulong msgId, BaseDiscordClient client, RequestOptions options) { - await client.ApiClient.DeleteMessageAsync(msg.Channel.Id, msg.Id, options).ConfigureAwait(false); + await client.ApiClient.DeleteMessageAsync(channelId, msgId, options).ConfigureAwait(false); } public static async Task AddReactionAsync(IMessage msg, IEmote emote, BaseDiscordClient client, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index 764431ae1..d95d45890 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -76,6 +76,11 @@ namespace Discord.WebSocket public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 1d68109ee..3d6f60db2 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -104,6 +104,11 @@ namespace Discord.WebSocket public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index f776c9956..9373c431a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -89,6 +89,11 @@ namespace Discord.WebSocket public Task DeleteMessagesAsync(IEnumerable messageIds, RequestOptions options = null) => ChannelHelper.DeleteMessagesAsync(this, Discord, messageIds, options); + public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, messageId, Discord, options); + public Task DeleteMessageAsync(IMessage message, RequestOptions options = null) + => ChannelHelper.DeleteMessageAsync(this, message.Id, Discord, options); + public Task TriggerTypingAsync(RequestOptions options = null) => ChannelHelper.TriggerTypingAsync(this, Discord, options); public IDisposable EnterTypingState(RequestOptions options = null) From 5f084adf94f3215b6d68c3a8b61cbfad7376f18c Mon Sep 17 00:00:00 2001 From: HelpfulStranger999 Date: Thu, 24 May 2018 19:17:19 -0500 Subject: [PATCH 28/49] Deprecates ReadMessages, introduces ViewChannel (#1033) * Deprecates ReadMessages, introduces ViewChannel * Adds period and comma somehow missed --- .../Entities/Permissions/GuildPermission.cs | 6 ++++-- .../Entities/Permissions/GuildPermissions.cs | 8 ++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs index 8469fd304..e90b4269e 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Discord { @@ -16,7 +16,9 @@ namespace Discord // Text AddReactions = 0x00_00_00_40, ViewAuditLog = 0x00_00_00_80, - ReadMessages = 0x00_00_04_00, + [Obsolete("Use ViewChannel instead.")] + ReadMessages = ViewChannel, + ViewChannel = 0x00_00_04_00, SendMessages = 0x00_00_08_00, SendTTSMessages = 0x00_00_10_00, ManageMessages = 0x00_00_20_00, diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index a880e62ca..dcebeb20d 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics; namespace Discord @@ -35,7 +36,10 @@ namespace Discord public bool ViewAuditLog => Permissions.GetValue(RawValue, GuildPermission.ViewAuditLog); /// If True, a user may join channels. - public bool ReadMessages => Permissions.GetValue(RawValue, GuildPermission.ReadMessages); + [Obsolete("Use ViewChannel instead.")] + public bool ReadMessages => ViewChannel; + /// If True, a user may view channels. + public bool ViewChannel => Permissions.GetValue(RawValue, GuildPermission.ViewChannel); /// If True, a user may send messages. public bool SendMessages => Permissions.GetValue(RawValue, GuildPermission.SendMessages); /// If True, a user may send text-to-speech messages. From bbbac85c46162d7bb44ecdd68194ab79db25037d Mon Sep 17 00:00:00 2001 From: Hawx Date: Fri, 25 May 2018 20:14:28 +0800 Subject: [PATCH 29/49] Update Dependencies and support NS2.0 builds (#1046) * Update deps, fix test warnings. * Support ns2.0 * Fix typo * Remove ns1.1 support * Net.Http and Net.Websockets.Client are not needed in ns2.0 * Move to net46 per volt * Remove ns1.3 constants --- Discord.Net.targets | 8 +-- .../02_commands_framework.csproj | 2 +- .../Discord.Net.Analyzers.csproj | 2 +- .../Discord.Net.Commands.csproj | 10 ++-- src/Discord.Net.Core/Discord.Net.Core.csproj | 10 ++-- .../Entities/Channels/IMessageChannel.cs | 3 +- src/Discord.Net.Core/Entities/Image.cs | 6 +-- .../Extensions/UserExtensions.cs | 2 - src/Discord.Net.Core/Logging/LogManager.cs | 23 ++++---- src/Discord.Net.Core/Logging/Logger.cs | 25 +++++---- src/Discord.Net.Core/Utils/DateTimeUtils.cs | 47 +---------------- src/Discord.Net.Core/Utils/SnowflakeUtils.cs | 4 +- src/Discord.Net.Rest/Discord.Net.Rest.csproj | 13 +++-- .../Entities/Channels/ChannelHelper.cs | 3 +- .../Entities/Channels/IRestMessageChannel.cs | 3 +- .../Entities/Channels/RestDMChannel.cs | 7 ++- .../Entities/Channels/RestGroupChannel.cs | 7 ++- .../Entities/Channels/RestTextChannel.cs | 7 ++- .../Channels/RpcVirtualMessageChannel.cs | 6 +-- .../Net/Converters/DiscordContractResolver.cs | 3 +- .../Net/Queue/RequestQueueBucket.cs | 2 +- src/Discord.Net.Rest/Net/RateLimitInfo.cs | 4 +- .../Discord.Net.WebSocket.csproj | 8 +-- .../DiscordSocketClient.cs | 2 +- .../DiscordVoiceApiClient.cs | 4 +- .../Channels/ISocketMessageChannel.cs | 3 +- .../Entities/Channels/SocketDMChannel.cs | 6 +-- .../Entities/Channels/SocketGroupChannel.cs | 8 +-- .../Entities/Channels/SocketTextChannel.cs | 8 +-- .../Net/DefaultUdpSocket.cs | 6 --- .../Net/DefaultUdpSocketProvider.cs | 10 +--- .../Net/DefaultWebSocketClient.cs | 13 ++--- .../Net/DefaultWebSocketClientProvider.cs | 10 +--- .../Discord.Net.Webhook.csproj | 4 +- .../DiscordWebhookClient.cs | 3 +- .../WebhookClientHelper.cs | 4 +- .../Discord.Net.Tests.csproj | 10 ++-- .../Tests.ChannelPermissions.cs | 2 +- test/Discord.Net.Tests/Tests.Channels.cs | 52 +++++++++---------- test/Discord.Net.Tests/Tests.Colors.cs | 3 +- test/Discord.Net.Tests/Tests.Emotes.cs | 3 +- test/Discord.Net.Tests/Tests.Permissions.cs | 8 +-- 42 files changed, 135 insertions(+), 229 deletions(-) diff --git a/Discord.Net.targets b/Discord.Net.targets index 958b2053f..079ec7749 100644 --- a/Discord.Net.targets +++ b/Discord.Net.targets @@ -16,13 +16,7 @@ $(VersionSuffix)-$(BuildNumber) build-$(BuildNumber) - - - $(DefineConstants);FILESYSTEM;DEFAULTUDPCLIENT;DEFAULTWEBSOCKET - - - $(DefineConstants);FORMATSTR;UNIXTIME;MSTRYBUFFER;UDPDISPOSE - + $(NoWarn);CS1573;CS1591 true diff --git a/samples/02_commands_framework/02_commands_framework.csproj b/samples/02_commands_framework/02_commands_framework.csproj index 77fdc65e1..f479ee0b0 100644 --- a/samples/02_commands_framework/02_commands_framework.csproj +++ b/samples/02_commands_framework/02_commands_framework.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj index 8ab398ff5..5da3d506d 100644 --- a/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj +++ b/src/Discord.Net.Analyzers/Discord.Net.Analyzers.csproj @@ -7,7 +7,7 @@ netstandard1.3 - + diff --git a/src/Discord.Net.Commands/Discord.Net.Commands.csproj b/src/Discord.Net.Commands/Discord.Net.Commands.csproj index eaac79a55..a754486dd 100644 --- a/src/Discord.Net.Commands/Discord.Net.Commands.csproj +++ b/src/Discord.Net.Commands/Discord.Net.Commands.csproj @@ -1,15 +1,19 @@ - + Discord.Net.Commands Discord.Commands A Discord.Net extension adding support for bot commands. - netstandard1.1 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - + + + + \ No newline at end of file diff --git a/src/Discord.Net.Core/Discord.Net.Core.csproj b/src/Discord.Net.Core/Discord.Net.Core.csproj index 321803114..6a58367e6 100644 --- a/src/Discord.Net.Core/Discord.Net.Core.csproj +++ b/src/Discord.Net.Core/Discord.Net.Core.csproj @@ -1,15 +1,15 @@ - + Discord.Net.Core Discord The core components for the Discord.Net library. - net45;netstandard1.1;netstandard1.3;netstandard2.0 - netstandard1.1;netstandard1.3;netstandard2.0 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - - + + diff --git a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs index 262b865ea..ef5a6fa7a 100644 --- a/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IMessageChannel.cs @@ -9,10 +9,9 @@ namespace Discord { /// Sends a message to this message channel. Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM /// Sends a file to this text channel, with an optional caption. Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, 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, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.Core/Entities/Image.cs b/src/Discord.Net.Core/Entities/Image.cs index c2c997365..3b946ce80 100644 --- a/src/Discord.Net.Core/Entities/Image.cs +++ b/src/Discord.Net.Core/Entities/Image.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Discord { /// @@ -15,7 +15,7 @@ namespace Discord { Stream = stream; } -#if FILESYSTEM + /// /// Create the image from a file path. /// @@ -27,6 +27,6 @@ namespace Discord { Stream = File.OpenRead(path); } -#endif + } } diff --git a/src/Discord.Net.Core/Extensions/UserExtensions.cs b/src/Discord.Net.Core/Extensions/UserExtensions.cs index 3296d00fd..951e8ca4b 100644 --- a/src/Discord.Net.Core/Extensions/UserExtensions.cs +++ b/src/Discord.Net.Core/Extensions/UserExtensions.cs @@ -32,7 +32,6 @@ namespace Discord return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); } -#if FILESYSTEM /// /// Sends a file to the user via DM. /// @@ -45,7 +44,6 @@ namespace Discord { return await (await user.GetOrCreateDMChannelAsync().ConfigureAwait(false)).SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); } -#endif public static Task BanAsync(this IGuildUser user, int pruneDays = 0, string reason = null, RequestOptions options = null) => user.Guild.AddBanAsync(user, pruneDays, reason, options); diff --git a/src/Discord.Net.Core/Logging/LogManager.cs b/src/Discord.Net.Core/Logging/LogManager.cs index 995a5d96a..a69519fa2 100644 --- a/src/Discord.Net.Core/Logging/LogManager.cs +++ b/src/Discord.Net.Core/Logging/LogManager.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord.Logging @@ -35,7 +35,7 @@ namespace Discord.Logging } catch { } } -#if FORMATSTR + public async Task LogAsync(LogSeverity severity, string source, FormattableString message, Exception ex = null) { try @@ -45,52 +45,49 @@ namespace Discord.Logging } catch { } } -#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 FORMATSTR + public Task ErrorAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Error, source, message, 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 FORMATSTR + public Task WarningAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Warning, source, message, 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 FORMATSTR public Task InfoAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Info, source, message, 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 FORMATSTR public Task VerboseAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Verbose, source, message, 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 FORMATSTR public Task DebugAsync(string source, FormattableString message, Exception ex = null) => LogAsync(LogSeverity.Debug, source, message, 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 a8d88b2b4..e71c56992 100644 --- a/src/Discord.Net.Core/Logging/Logger.cs +++ b/src/Discord.Net.Core/Logging/Logger.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord.Logging @@ -20,54 +20,53 @@ 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 FORMATSTR 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 FORMATSTR + public Task ErrorAsync(FormattableString message, Exception exception = null) => _manager.ErrorAsync(Name, message, 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 FORMATSTR + public Task WarningAsync(FormattableString message, Exception exception = null) => _manager.WarningAsync(Name, message, 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 FORMATSTR + public Task InfoAsync(FormattableString message, Exception exception = null) => _manager.InfoAsync(Name, message, 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 FORMATSTR + public Task VerboseAsync(FormattableString message, Exception exception = null) => _manager.VerboseAsync(Name, message, 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 FORMATSTR + public Task DebugAsync(FormattableString message, Exception exception = null) => _manager.DebugAsync(Name, message, exception); -#endif + } } diff --git a/src/Discord.Net.Core/Utils/DateTimeUtils.cs b/src/Discord.Net.Core/Utils/DateTimeUtils.cs index af2126853..e2a8faa75 100644 --- a/src/Discord.Net.Core/Utils/DateTimeUtils.cs +++ b/src/Discord.Net.Core/Utils/DateTimeUtils.cs @@ -1,57 +1,14 @@ -using System; +using System; namespace Discord { //Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs internal static class DateTimeUtils { -#if !UNIXTIME - private const long UnixEpochTicks = 621_355_968_000_000_000; - private const long UnixEpochSeconds = 62_135_596_800; - private const long UnixEpochMilliseconds = 62_135_596_800_000; -#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 FromUnixSeconds(long seconds) - { -#if UNIXTIME - return DateTimeOffset.FromUnixTimeSeconds(seconds); -#else - long ticks = seconds * TimeSpan.TicksPerSecond + UnixEpochTicks; - return new DateTimeOffset(ticks, TimeSpan.Zero); -#endif - } - public static DateTimeOffset FromUnixMilliseconds(long milliseconds) - { -#if UNIXTIME - return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); -#else - long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; - return new DateTimeOffset(ticks, TimeSpan.Zero); -#endif - } - - public static long ToUnixSeconds(DateTimeOffset dto) - { -#if UNIXTIME - return dto.ToUnixTimeSeconds(); -#else - long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerSecond; - return seconds - UnixEpochSeconds; -#endif - } - public static long ToUnixMilliseconds(DateTimeOffset dto) - { -#if UNIXTIME - return dto.ToUnixTimeMilliseconds(); -#else - long milliseconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; - return milliseconds - UnixEpochMilliseconds; -#endif - } + } } diff --git a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs index c9d0d130b..eecebfb24 100644 --- a/src/Discord.Net.Core/Utils/SnowflakeUtils.cs +++ b/src/Discord.Net.Core/Utils/SnowflakeUtils.cs @@ -5,8 +5,8 @@ namespace Discord public static class SnowflakeUtils { public static DateTimeOffset FromSnowflake(ulong value) - => DateTimeUtils.FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); + => DateTimeOffset.FromUnixTimeMilliseconds((long)((value >> 22) + 1420070400000UL)); public static ulong ToSnowflake(DateTimeOffset value) - => ((ulong)DateTimeUtils.ToUnixMilliseconds(value) - 1420070400000UL) << 22; + => ((ulong)value.ToUnixTimeMilliseconds() - 1420070400000UL) << 22; } } diff --git a/src/Discord.Net.Rest/Discord.Net.Rest.csproj b/src/Discord.Net.Rest/Discord.Net.Rest.csproj index 0eb07a4b2..75b69bd04 100644 --- a/src/Discord.Net.Rest/Discord.Net.Rest.csproj +++ b/src/Discord.Net.Rest/Discord.Net.Rest.csproj @@ -1,20 +1,19 @@ - + Discord.Net.Rest Discord.Rest A core Discord.Net library containing the REST client and models. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 - - - + + - + diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index dbbabbd72..6084ca4dc 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -168,7 +168,6 @@ namespace Discord.Rest return RestUserMessage.Create(client, channel, client.CurrentUser, model); } -#if FILESYSTEM public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, string filePath, string text, bool isTTS, Embed embed, RequestOptions options) { @@ -176,7 +175,7 @@ namespace Discord.Rest using (var file = File.OpenRead(filePath)) return await SendFileAsync(channel, client, file, filename, text, isTTS, embed, options).ConfigureAwait(false); } -#endif + public static async Task SendFileAsync(IMessageChannel channel, BaseDiscordClient client, Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) { diff --git a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs index b6f891f40..e0095c7b1 100644 --- a/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/IRestMessageChannel.cs @@ -8,10 +8,9 @@ namespace Discord.Rest { /// Sends a message to this message channel. new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, 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, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs index 21cd579e6..64efcf24b 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs @@ -65,10 +65,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -126,10 +126,9 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 901016a6b..367abfb4a 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -83,10 +83,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -136,10 +136,9 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index 841aad666..b01a88210 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -60,10 +60,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -131,10 +131,9 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options).ConfigureAwait(false); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs index 8f69388d4..e8b939e65 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RpcVirtualMessageChannel.cs @@ -35,11 +35,10 @@ namespace Discord.Rest public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) + => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); public Task DeleteMessageAsync(ulong messageId, RequestOptions options = null) @@ -86,10 +85,9 @@ namespace Discord.Rest async Task> IMessageChannel.GetPinnedMessagesAsync(RequestOptions options) => await GetPinnedMessagesAsync(options); -#if FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs index 9213c5d75..8a3c1037b 100644 --- a/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs +++ b/src/Discord.Net.Rest/Net/Converters/DiscordContractResolver.cs @@ -1,4 +1,4 @@ -using Discord.API; +using Discord.API; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using System; @@ -25,7 +25,6 @@ namespace Discord.Net.Converters if (converter != null) { property.Converter = converter; - property.MemberConverter = converter; } } else diff --git a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs index 3346681b5..c4f5996c5 100644 --- a/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs +++ b/src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs @@ -230,7 +230,7 @@ namespace Discord.Net.Queue #endif } - var now = DateTimeUtils.ToUnixSeconds(DateTimeOffset.UtcNow); + var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); DateTimeOffset? resetTick = null; //Using X-RateLimit-Remaining causes a race condition diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index 9421221ed..a517f290c 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Discord.Net @@ -21,7 +21,7 @@ namespace Discord.Net Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && int.TryParse(temp, out var remaining) ? remaining : (int?)null; Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && - int.TryParse(temp, out var reset) ? DateTimeUtils.FromUnixSeconds(reset) : (DateTimeOffset?)null; + int.TryParse(temp, out var reset) ? DateTimeOffset.FromUnixTimeSeconds(reset) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) && int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; Lag = headers.TryGetValue("Date", out temp) && diff --git a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj index 251cdb18b..ddd3b7954 100644 --- a/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj +++ b/src/Discord.Net.WebSocket/Discord.Net.WebSocket.csproj @@ -1,11 +1,11 @@ - + Discord.Net.WebSocket Discord.WebSocket A core Discord.Net library containing the WebSocket client and models. - net45;netstandard1.1;netstandard1.3 - netstandard1.1;netstandard1.3 + net46;netstandard1.3;netstandard2.0 + netstandard1.3;netstandard2.0 true @@ -13,6 +13,6 @@ - + diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 72b0b022b..01322a3cc 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -365,7 +365,7 @@ namespace Discord.WebSocket await ApiClient.SendStatusUpdateAsync( status, status == UserStatus.AFK, - statusSince != null ? DateTimeUtils.ToUnixMilliseconds(_statusSince.Value) : (long?)null, + statusSince != null ? _statusSince.Value.ToUnixTimeMilliseconds() : (long?)null, gameModel).ConfigureAwait(false); } diff --git a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs index 25dc2cf7b..0eb92caed 100644 --- a/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs +++ b/src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Discord.API; using Discord.API.Voice; using Discord.Net.Converters; @@ -129,7 +129,7 @@ namespace Discord.Audio //WebSocket public async Task SendHeartbeatAsync(RequestOptions options = null) { - await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false); + await SendAsync(VoiceOpCode.Heartbeat, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), options: options).ConfigureAwait(false); } public async Task SendIdentityAsync(ulong userId, string sessionId, string token) { diff --git a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs index 971dbe8f1..5fef7e4cd 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/ISocketMessageChannel.cs @@ -12,10 +12,9 @@ namespace Discord.WebSocket /// Sends a message to this message channel. new Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null); -#if FILESYSTEM /// Sends a file to this text channel, with an optional caption. new Task SendFileAsync(string filePath, string text = null, bool isTTS = false, Embed embed = null, 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, Embed embed = null, RequestOptions options = null); diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs index d95d45890..11b7ce25a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs @@ -69,10 +69,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -135,10 +135,8 @@ 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 FILESYSTEM async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 3d6f60db2..81b5cfbf3 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -97,10 +97,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -199,10 +199,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 FILESYSTEM + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 9373c431a..dc68044fe 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -77,10 +77,10 @@ namespace Discord.WebSocket public Task SendMessageAsync(string text = null, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendMessageAsync(this, Discord, text, isTTS, embed, options); -#if FILESYSTEM + public Task SendFileAsync(string filePath, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, filePath, text, isTTS, embed, options); -#endif + public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, Embed embed = null, RequestOptions options = null) => ChannelHelper.SendFileAsync(this, Discord, stream, filename, text, isTTS, embed, options); @@ -159,10 +159,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 FILESYSTEM + async Task IMessageChannel.SendFileAsync(string filePath, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(filePath, text, isTTS, embed, options).ConfigureAwait(false); -#endif + async Task IMessageChannel.SendFileAsync(Stream stream, string filename, string text, bool isTTS, Embed embed, RequestOptions options) => await SendFileAsync(stream, filename, text, isTTS, embed, options).ConfigureAwait(false); async Task IMessageChannel.SendMessageAsync(string text, bool isTTS, Embed embed, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs index 6a6194397..251a761d4 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocket.cs @@ -1,4 +1,3 @@ -#if DEFAULTUDPCLIENT using System; using System.Net; using System.Net.Sockets; @@ -85,11 +84,7 @@ namespace Discord.Net.Udp if (_udp != null) { -#if UDPDISPOSE try { _udp.Dispose(); } -#else - try { _udp.Close(); } -#endif catch { } _udp = null; } @@ -132,4 +127,3 @@ namespace Discord.Net.Udp } } } -#endif \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs index 82b6ec4c0..d701fa79a 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultUdpSocketProvider.cs @@ -4,7 +4,6 @@ namespace Discord.Net.Udp { public static class DefaultUdpSocketProvider { -#if DEFAULTUDPCLIENT public static readonly UdpSocketProvider Instance = () => { try @@ -16,12 +15,5 @@ namespace Discord.Net.Udp 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 a250acec9..c60368da0 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs @@ -1,4 +1,3 @@ -#if DEFAULTWEBSOCKET using System; using System.Collections.Generic; using System.ComponentModel; @@ -209,14 +208,9 @@ namespace Discord.Net.WebSockets //Use the internal buffer if we can get it resultCount = (int)stream.Length; -#if MSTRYBUFFER - if (stream.TryGetBuffer(out var streamBuffer)) - result = streamBuffer.Array; - else - result = stream.ToArray(); -#else - result = stream.GetBuffer(); -#endif + + result = stream.TryGetBuffer(out var streamBuffer) ? streamBuffer.Array : stream.ToArray(); + } } else @@ -248,4 +242,3 @@ namespace Discord.Net.WebSockets } } } -#endif \ No newline at end of file diff --git a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs index 68bd67c5b..2d66d5900 100644 --- a/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs +++ b/src/Discord.Net.WebSocket/Net/DefaultWebSocketClientProvider.cs @@ -5,7 +5,6 @@ namespace Discord.Net.WebSockets { public static class DefaultWebSocketProvider { -#if DEFAULTWEBSOCKET public static readonly WebSocketProvider Instance = Create(); public static WebSocketProvider Create(IWebProxy proxy = null) @@ -22,12 +21,5 @@ namespace Discord.Net.WebSockets } }; } -#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 +} diff --git a/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj b/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj index 7c224e01e..ba7bbcff8 100644 --- a/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj +++ b/src/Discord.Net.Webhook/Discord.Net.Webhook.csproj @@ -1,10 +1,10 @@ - + Discord.Net.Webhook Discord.Webhook A core Discord.Net library containing the Webhook client and models. - netstandard1.1 + netstandard1.3 diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 2dea1c1d1..67a5462be 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -67,12 +67,11 @@ namespace Discord.Webhook string username = null, string avatarUrl = null, RequestOptions options = null) => WebhookClientHelper.SendMessageAsync(this, text, isTTS, embeds, username, avatarUrl, options); -#if FILESYSTEM /// Send a message to the channel for this webhook with an attachment. Returns the ID of the created message. public Task SendFileAsync(string filePath, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) => WebhookClientHelper.SendFileAsync(this, filePath, text, isTTS, embeds, username, avatarUrl, options); -#endif + /// Send a message to the channel for this webhook with an attachment. Returns the ID of the created message. public Task SendFileAsync(Stream stream, string filename, string text, bool isTTS = false, IEnumerable embeds = null, string username = null, string avatarUrl = null, RequestOptions options = null) diff --git a/src/Discord.Net.Webhook/WebhookClientHelper.cs b/src/Discord.Net.Webhook/WebhookClientHelper.cs index 1116662a6..d3cac9703 100644 --- a/src/Discord.Net.Webhook/WebhookClientHelper.cs +++ b/src/Discord.Net.Webhook/WebhookClientHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -33,7 +33,6 @@ namespace Discord.Webhook var model = await client.ApiClient.CreateWebhookMessageAsync(client.Webhook.Id, args, options: options).ConfigureAwait(false); return model.Id; } -#if FILESYSTEM public static async Task SendFileAsync(DiscordWebhookClient client, string filePath, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, RequestOptions options) { @@ -41,7 +40,6 @@ namespace Discord.Webhook using (var file = File.OpenRead(filePath)) return await SendFileAsync(client, file, filename, text, isTTS, embeds, username, avatarUrl, options).ConfigureAwait(false); } -#endif public static async Task SendFileAsync(DiscordWebhookClient client, Stream stream, string filename, string text, bool isTTS, IEnumerable embeds, string username, string avatarUrl, RequestOptions options) { diff --git a/test/Discord.Net.Tests/Discord.Net.Tests.csproj b/test/Discord.Net.Tests/Discord.Net.Tests.csproj index 204dca5c4..60491a96f 100644 --- a/test/Discord.Net.Tests/Discord.Net.Tests.csproj +++ b/test/Discord.Net.Tests/Discord.Net.Tests.csproj @@ -21,10 +21,10 @@ - - - - - + + + + + diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs index b37a1195e..da969b320 100644 --- a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -84,7 +84,7 @@ namespace Discord Assert.Equal(groupChannel, ChannelPermissions.Group.RawValue); return Task.CompletedTask; } - + [Fact] public Task TestChannelPermissionModify() { // test channel permission modify diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs index b528ca5fb..cd629faa4 100644 --- a/test/Discord.Net.Tests/Tests.Channels.cs +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -49,35 +49,35 @@ namespace Discord } private static void CheckTextChannels(RestGuild guild, params RestTextChannel[] textChannels) { - Assert.Equal(textChannels.Length, 5); + Assert.Equal(5, textChannels.Length); Assert.All(textChannels, x => { Assert.NotNull(x); - Assert.NotEqual(x.Id, 0UL); + Assert.NotEqual(0UL, x.Id); 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(); + var text1 = textChannels.FirstOrDefault(x => x.Name == "text1"); + var text2 = textChannels.FirstOrDefault(x => x.Name == "text2"); + var text3 = textChannels.FirstOrDefault(x => x.Name == "text3"); + var text4 = textChannels.FirstOrDefault(x => x.Name == "text4"); + var text5 = textChannels.FirstOrDefault(x => x.Name == "text5"); Assert.NotNull(text1); //Assert.True(text1.Id == guild.DefaultChannelId); - Assert.Equal(text1.Position, 1); - Assert.Equal(text1.Topic, "Topic1"); + Assert.Equal(1, text1.Position); + Assert.Equal("Topic1", text1.Topic); Assert.NotNull(text2); - Assert.Equal(text2.Position, 2); + Assert.Equal(2, text2.Position); Assert.Null(text2.Topic); Assert.NotNull(text3); - Assert.Equal(text3.Topic, "Topic2"); + Assert.Equal("Topic2", text3.Topic); Assert.NotNull(text4); - Assert.Equal(text4.Position, 3); - Assert.Equal(text4.Topic, "Topic2"); + Assert.Equal(3, text4.Position); + Assert.Equal("Topic2", text4.Topic); Assert.NotNull(text5); Assert.Null(text5.Topic); @@ -114,31 +114,31 @@ namespace Discord } private static void CheckVoiceChannels(params RestVoiceChannel[] voiceChannels) { - Assert.Equal(voiceChannels.Length, 3); + Assert.Equal(3, voiceChannels.Length); Assert.All(voiceChannels, x => { Assert.NotNull(x); - Assert.NotEqual(x.Id, 0UL); - Assert.NotEqual(x.UserLimit, 0); + Assert.NotEqual(0UL, x.Id); + Assert.NotEqual(0, x.UserLimit); 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(); - var voice3 = voiceChannels.Where(x => x.Name == "voice3").FirstOrDefault(); + var voice1 = voiceChannels.FirstOrDefault(x => x.Name == "voice1"); + var voice2 = voiceChannels.FirstOrDefault(x => x.Name == "voice2"); + var voice3 = voiceChannels.FirstOrDefault(x => x.Name == "voice3"); Assert.NotNull(voice1); - Assert.Equal(voice1.Bitrate, 96000); - Assert.Equal(voice1.Position, 1); + Assert.Equal(96000, voice1.Bitrate); + Assert.Equal(1, voice1.Position); Assert.NotNull(voice2); - Assert.Equal(voice2.UserLimit, null); + Assert.Null(voice2.UserLimit); Assert.NotNull(voice3); - Assert.Equal(voice3.Bitrate, 8000); - Assert.Equal(voice3.Position, 1); - Assert.Equal(voice3.UserLimit, 16); + Assert.Equal(8000, voice3.Bitrate); + Assert.Equal(1, voice3.Position); + Assert.Equal(16, voice3.UserLimit); } } -} \ No newline at end of file +} diff --git a/test/Discord.Net.Tests/Tests.Colors.cs b/test/Discord.Net.Tests/Tests.Colors.cs index 591778972..10b0bbdac 100644 --- a/test/Discord.Net.Tests/Tests.Colors.cs +++ b/test/Discord.Net.Tests/Tests.Colors.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace Discord @@ -12,6 +12,7 @@ namespace Discord Assert.Equal(uint.MinValue, new Color(uint.MinValue).RawValue); Assert.Equal(uint.MaxValue, new Color(uint.MaxValue).RawValue); } + [Fact] public void Color_Default() { Assert.Equal(0u, Color.Default.RawValue); diff --git a/test/Discord.Net.Tests/Tests.Emotes.cs b/test/Discord.Net.Tests/Tests.Emotes.cs index 334975ce4..eeadbddf8 100644 --- a/test/Discord.Net.Tests/Tests.Emotes.cs +++ b/test/Discord.Net.Tests/Tests.Emotes.cs @@ -1,4 +1,4 @@ -using System; +using System; using Xunit; namespace Discord @@ -34,6 +34,7 @@ namespace Discord Assert.Equal(DateTimeOffset.FromUnixTimeMilliseconds(1514056829775), emote.CreatedAt); Assert.EndsWith("gif", emote.Url); } + [Fact] public void Test_Invalid_Amimated_Emote_Parse() { Assert.False(Emote.TryParse("", out _)); diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index e22659d15..6e71da1b1 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -25,15 +25,15 @@ namespace Discord // check that toggling the bit works Permissions.UnsetFlag(ref rawValue, flagValue); - Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); + Assert.False(Permissions.GetValue(rawValue, flagValue)); Permissions.SetFlag(ref rawValue, flagValue); - Assert.Equal(true, Permissions.GetValue(rawValue, flagValue)); + Assert.True(Permissions.GetValue(rawValue, flagValue)); // do the same, but with the SetValue method Permissions.SetValue(ref rawValue, true, flagValue); - Assert.Equal(true, Permissions.GetValue(rawValue, flagValue)); + Assert.True(Permissions.GetValue(rawValue, flagValue)); Permissions.SetValue(ref rawValue, false, flagValue); - Assert.Equal(false, Permissions.GetValue(rawValue, flagValue)); + Assert.False(Permissions.GetValue(rawValue, flagValue)); } /// From fa759a22eac25183eb0fecf6cd0fbbf7f476fa11 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Fri, 25 May 2018 06:38:59 -0700 Subject: [PATCH 30/49] Remove Build Warnings for Obsolete Guild Permission Read Messages (#1067) - Replaces the usages of `ReadMessages` with `ViewChannel` - Renames the read message parameters of `GuildPermissions#Modify` to be view channel as well --- .../Entities/Permissions/GuildPermissions.cs | 12 ++++++------ test/Discord.Net.Tests/Tests.GuildPermissions.cs | 10 +++++----- test/Discord.Net.Tests/Tests.Permissions.cs | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index dcebeb20d..aa914eda1 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -87,7 +87,7 @@ namespace Discord private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, bool? addReactions = null, bool? viewAuditLog = null, - bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? viewChannel = 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, @@ -103,7 +103,7 @@ namespace Discord Permissions.SetValue(ref value, manageGuild, GuildPermission.ManageGuild); Permissions.SetValue(ref value, addReactions, GuildPermission.AddReactions); Permissions.SetValue(ref value, viewAuditLog, GuildPermission.ViewAuditLog); - Permissions.SetValue(ref value, readMessages, GuildPermission.ReadMessages); + Permissions.SetValue(ref value, viewChannel, GuildPermission.ViewChannel); Permissions.SetValue(ref value, sendMessages, GuildPermission.SendMessages); Permissions.SetValue(ref value, sendTTSMessages, GuildPermission.SendTTSMessages); Permissions.SetValue(ref value, manageMessages, GuildPermission.ManageMessages); @@ -131,14 +131,14 @@ namespace Discord public GuildPermissions(bool createInstantInvite = false, bool kickMembers = false, bool banMembers = false, bool administrator = false, bool manageChannels = false, bool manageGuild = false, bool addReactions = false, bool viewAuditLog = false, - bool readMessages = false, bool sendMessages = false, bool sendTTSMessages = false, bool manageMessages = false, + bool viewChannel = 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: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers, administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions, - viewAuditLog: viewAuditLog, readMessages: readMessages, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages, + viewAuditLog: viewAuditLog, viewChannel: viewChannel, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages, manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory, mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers, deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname, @@ -149,13 +149,13 @@ namespace Discord 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? viewAuditLog = null, - bool? readMessages = null, bool? sendMessages = null, bool? sendTTSMessages = null, bool? manageMessages = null, + bool? viewChannel = 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, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, - viewAuditLog, readMessages, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, + viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs index a562f4afb..2b7c39341 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -138,12 +138,12 @@ namespace Discord // individual permission test - perm = perm.Modify(readMessages: true); - Assert.True(perm.ReadMessages); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessages); + perm = perm.Modify(viewChannel: true); + Assert.True(perm.ViewChannel); + Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewChannel); - perm = perm.Modify(readMessages: false); - Assert.False(perm.ReadMessages); + perm = perm.Modify(viewChannel: false); + Assert.False(perm.ViewChannel); Assert.Equal(GuildPermissions.None.RawValue, perm.RawValue); diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index 6e71da1b1..0516337da 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -280,7 +280,7 @@ namespace Discord TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.ViewAuditLog, false); - TestHelper(value, GuildPermission.ReadMessages, false); + TestHelper(value, GuildPermission.ViewChannel, false); TestHelper(value, GuildPermission.SendMessages, false); TestHelper(value, GuildPermission.SendTTSMessages, false); TestHelper(value, GuildPermission.ManageMessages, false); @@ -323,7 +323,7 @@ namespace Discord TestHelper(value, GuildPermission.ManageGuild, true); TestHelper(value, GuildPermission.AddReactions, true); TestHelper(value, GuildPermission.ViewAuditLog, true); - TestHelper(value, GuildPermission.ReadMessages, true); + TestHelper(value, GuildPermission.ViewChannel, true); TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.ManageMessages, true); @@ -367,7 +367,7 @@ namespace Discord TestHelper(value, GuildPermission.ManageGuild, false); TestHelper(value, GuildPermission.AddReactions, false); TestHelper(value, GuildPermission.ViewAuditLog, false); - TestHelper(value, GuildPermission.ReadMessages, false); + TestHelper(value, GuildPermission.ViewChannel, false); TestHelper(value, GuildPermission.SendMessages, true); TestHelper(value, GuildPermission.SendTTSMessages, true); TestHelper(value, GuildPermission.ManageMessages, false); From 4d8764e124cef12af4b0944d185ec22d9683c520 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Sat, 26 May 2018 14:11:49 +0100 Subject: [PATCH 31/49] Refactor RoleInfo types into separate classes - Fixes indentation with a few files (looks like a bad copy+paste job) - Renames RoleInfo to RoleEditInfo - Moves RoleInfo from MemberRoleAuditLogData into its own class All of this should improve docs. --- .../DataTypes/ChannelUpdateAuditLogData.cs | 4 ++-- .../DataTypes/MemberRoleAuditLogData.cs | 20 +++--------------- .../AuditLogs/DataTypes/MemberRoleEditInfo.cs | 16 ++++++++++++++ .../DataTypes/RoleCreateAuditLogData.cs | 6 +++--- .../DataTypes/RoleDeleteAuditLogData.cs | 6 +++--- .../AuditLogs/DataTypes/RoleEditInfo.cs | 21 +++++++++++++++++++ .../Entities/AuditLogs/DataTypes/RoleInfo.cs | 21 ------------------- .../DataTypes/RoleUpdateAuditLogData.cs | 10 ++++----- 8 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs create mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs delete mode 100644 src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs index f3403138d..491cb5717 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs @@ -39,7 +39,7 @@ namespace Discord.Rest } public ulong ChannelId { get; } - public ChannelInfo Before { get; set; } - public ChannelInfo After { get; set; } + public ChannelInfo Before { get; } + public ChannelInfo After { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs index b0f0a1fe1..3bcbce440 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs @@ -9,7 +9,7 @@ namespace Discord.Rest { public class MemberRoleAuditLogData : IAuditLogData { - private MemberRoleAuditLogData(IReadOnlyCollection roles, IUser target) + private MemberRoleAuditLogData(IReadOnlyCollection roles, IUser target) { Roles = roles; Target = target; @@ -21,7 +21,7 @@ namespace Discord.Rest var roleInfos = changes.SelectMany(x => x.NewValue.ToObject(), (model, role) => new { model.ChangedProperty, Role = role }) - .Select(x => new RoleInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) + .Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) .ToList(); var userInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); @@ -30,21 +30,7 @@ namespace Discord.Rest return new MemberRoleAuditLogData(roleInfos.ToReadOnlyCollection(), user); } - public IReadOnlyCollection Roles { get; } + public IReadOnlyCollection Roles { get; } public IUser Target { get; } - - public struct RoleInfo - { - internal RoleInfo(string name, ulong roleId, bool added) - { - Name = name; - RoleId = roleId; - Added = added; - } - - public string Name { get; } - public ulong RoleId { get; } - public bool Added { get; } - } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs new file mode 100644 index 000000000..4838b75c9 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleEditInfo.cs @@ -0,0 +1,16 @@ +namespace Discord.Rest +{ + public struct MemberRoleEditInfo + { + internal MemberRoleEditInfo(string name, ulong roleId, bool added) + { + Name = name; + RoleId = roleId; + Added = added; + } + + public string Name { get; } + public ulong RoleId { get; } + public bool Added { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs index aa951d6e7..dcc1c6ab6 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleCreateAuditLogData : IAuditLogData { - private RoleCreateAuditLogData(ulong id, RoleInfo props) + private RoleCreateAuditLogData(ulong id, RoleEditInfo props) { RoleId = id; Properties = props; @@ -38,10 +38,10 @@ namespace Discord.Rest permissions = new GuildPermissions(permissionsRaw.Value); return new RoleCreateAuditLogData(entry.TargetId.Value, - new RoleInfo(color, mentionable, hoist, name, permissions)); + new RoleEditInfo(color, mentionable, hoist, name, permissions)); } public ulong RoleId { get; } - public RoleInfo Properties { get; } + public RoleEditInfo Properties { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs index e90d70d4d..263909daf 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleDeleteAuditLogData : IAuditLogData { - private RoleDeleteAuditLogData(ulong id, RoleInfo props) + private RoleDeleteAuditLogData(ulong id, RoleEditInfo props) { RoleId = id; Properties = props; @@ -38,10 +38,10 @@ namespace Discord.Rest permissions = new GuildPermissions(permissionsRaw.Value); return new RoleDeleteAuditLogData(entry.TargetId.Value, - new RoleInfo(color, mentionable, hoist, name, permissions)); + new RoleEditInfo(color, mentionable, hoist, name, permissions)); } public ulong RoleId { get; } - public RoleInfo Properties { get; } + public RoleEditInfo Properties { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs new file mode 100644 index 000000000..186ea8d11 --- /dev/null +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleEditInfo.cs @@ -0,0 +1,21 @@ +namespace Discord.Rest +{ + public struct RoleEditInfo + { + internal RoleEditInfo(Color? color, bool? mentionable, bool? hoist, string name, + GuildPermissions? permissions) + { + Color = color; + Mentionable = mentionable; + Hoist = hoist; + Name = name; + Permissions = permissions; + } + + public Color? Color { get; } + public bool? Mentionable { get; } + public bool? Hoist { get; } + public string Name { get; } + public GuildPermissions? Permissions { get; } + } +} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs deleted file mode 100644 index 2208990e6..000000000 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Discord.Rest -{ - public struct RoleInfo - { - internal RoleInfo(Color? color, bool? mentionable, bool? hoist, string name, - GuildPermissions? permissions) - { - Color = color; - Mentionable = mentionable; - Hoist = hoist; - Name = name; - Permissions = permissions; - } - - public Color? Color { get; } - public bool? Mentionable { get; } - public bool? Hoist { get; } - public string Name { get; } - public GuildPermissions? Permissions { get; } - } -} diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs index be484e2d5..b645ef7ae 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs @@ -7,7 +7,7 @@ namespace Discord.Rest { public class RoleUpdateAuditLogData : IAuditLogData { - private RoleUpdateAuditLogData(ulong id, RoleInfo oldProps, RoleInfo newProps) + private RoleUpdateAuditLogData(ulong id, RoleEditInfo oldProps, RoleEditInfo newProps) { RoleId = id; Before = oldProps; @@ -49,14 +49,14 @@ namespace Discord.Rest if (newPermissionsRaw.HasValue) newPermissions = new GuildPermissions(newPermissionsRaw.Value); - var oldProps = new RoleInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions); - var newProps = new RoleInfo(newColor, newMentionable, newHoist, newName, newPermissions); + var oldProps = new RoleEditInfo(oldColor, oldMentionable, oldHoist, oldName, oldPermissions); + var newProps = new RoleEditInfo(newColor, newMentionable, newHoist, newName, newPermissions); return new RoleUpdateAuditLogData(entry.TargetId.Value, oldProps, newProps); } public ulong RoleId { get; } - public RoleInfo Before { get; } - public RoleInfo After { get; } + public RoleEditInfo Before { get; } + public RoleEditInfo After { get; } } } From f9cbff5e42c04aa8a0fe87377ba68aa7d748d873 Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 26 May 2018 11:06:35 -0700 Subject: [PATCH 32/49] Fix #995, Move Category Implementation from IGuildChannel to INestedChannel (#1004) * Fix #995 ICategoryChannel.CategoryID throws NotSupportedException * Add tests * change run mode of TestChannelCategories * Add throw for GetCategoryAsync * Add xml doc explaining why exception is thrown * Add test coverage for text and voice channel categories * initial implementation of INestedChannel * more implementation of INestedChannel design * Add case in RestChannel Create for Category type * set the CategoryID for RestVoiceChannel * rewrite channel category tests to work with existing pattern * remove outdated todo * Make IVoiceChannel implement INestedChannel * remove redundant interface implementation * Add c#7 feature from feedback * Remove redundant GetCategoryAsync methods from socket entities * Added configureawait to async methods * change signature of interface GetCategoryAsync * Add check for cachemode in rest channel GetCategory * remove redundant IGuildChannel interface from ITextChannel and IVoiceChannel --- .../Entities/Channels/ICategoryChannel.cs | 2 +- .../Entities/Channels/IGuildChannel.cs | 8 +- .../Entities/Channels/INestedChannel.cs | 16 ++++ .../Entities/Channels/ITextChannel.cs | 6 +- .../Entities/Channels/IVoiceChannel.cs | 6 +- .../Entities/Channels/ChannelHelper.cs | 10 +++ .../Entities/Channels/RestCategoryChannel.cs | 6 +- .../Entities/Channels/RestChannel.cs | 4 +- .../Entities/Channels/RestGuildChannel.cs | 11 +-- .../Entities/Channels/RestTextChannel.cs | 23 ++++-- .../Entities/Channels/RestVoiceChannel.cs | 16 +++- .../Channels/SocketCategoryChannel.cs | 2 +- .../Entities/Channels/SocketGuildChannel.cs | 13 +--- .../Entities/Channels/SocketTextChannel.cs | 9 ++- .../Entities/Channels/SocketVoiceChannel.cs | 13 +++- test/Discord.Net.Tests/Tests.Channels.cs | 74 +++++++++++++++++++ 16 files changed, 168 insertions(+), 51 deletions(-) create mode 100644 src/Discord.Net.Core/Entities/Channels/INestedChannel.cs diff --git a/src/Discord.Net.Core/Entities/Channels/ICategoryChannel.cs b/src/Discord.Net.Core/Entities/Channels/ICategoryChannel.cs index 0f7f5aa62..c004cafd5 100644 --- a/src/Discord.Net.Core/Entities/Channels/ICategoryChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ICategoryChannel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs index c9841cb15..6514d46cd 100644 --- a/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IGuildChannel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -9,10 +9,6 @@ namespace Discord /// Gets the position of this channel in the guild's channel list, relative to others of the same type. int Position { get; } - /// Gets the parentid (category) of this channel in the guild's channel list. - ulong? CategoryId { get; } - /// Gets the parent channel (category) of this channel. - Task GetCategoryAsync(); /// Gets the guild this channel is a member of. IGuild Guild { get; } /// Gets the id of the guild this channel is a member of. @@ -49,4 +45,4 @@ namespace Discord /// Gets a user in this channel with the provided id. new Task GetUserAsync(ulong id, CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); } -} \ No newline at end of file +} diff --git a/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs b/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs new file mode 100644 index 000000000..c8d2bcaaf --- /dev/null +++ b/src/Discord.Net.Core/Entities/Channels/INestedChannel.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Discord +{ + /// + /// A type of guild channel that can be nested within a category. + /// Contains a CategoryId that is set to the parent category, if it is set. + /// + public interface INestedChannel : IGuildChannel + { + /// Gets the parentid (category) of this channel in the guild's channel list. + ulong? CategoryId { get; } + /// Gets the parent channel (category) of this channel, if it is set. If unset, returns null. + Task GetCategoryAsync(CacheMode mode = CacheMode.AllowDownload, RequestOptions options = null); + } +} diff --git a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs index 7c6ec3908..2aa070b03 100644 --- a/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/ITextChannel.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; namespace Discord { - public interface ITextChannel : IMessageChannel, IMentionable, IGuildChannel + public interface ITextChannel : IMessageChannel, IMentionable, INestedChannel { /// Checks if the channel is NSFW. bool IsNsfw { get; } @@ -28,4 +28,4 @@ namespace Discord /// Gets the webhooks for this text channel. Task> GetWebhooksAsync(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 e2a2ad8eb..2e345bfda 100644 --- a/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IVoiceChannel.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Threading.Tasks; namespace Discord { - public interface IVoiceChannel : IGuildChannel, IAudioChannel + public interface IVoiceChannel : INestedChannel, IAudioChannel { /// Gets the bitrate, in bits per second, clients in this voice channel are requested to use. int Bitrate { get; } @@ -13,4 +13,4 @@ namespace Discord /// Modifies this voice channel. Task ModifyAsync(Action func, RequestOptions options = null); } -} \ No newline at end of file +} diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 6084ca4dc..4047b7014 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -316,6 +316,16 @@ namespace Discord.Rest return models.Select(x => RestWebhook.Create(client, channel, x)) .ToImmutableArray(); } + // Categories + public static async Task GetCategoryAsync(INestedChannel channel, BaseDiscordClient client, RequestOptions options) + { + // if no category id specified, return null + if (!channel.CategoryId.HasValue) + return null; + // CategoryId will contain a value here + var model = await client.ApiClient.GetChannelAsync(channel.CategoryId.Value, options).ConfigureAwait(false); + return RestCategoryChannel.Create(client, model) as ICategoryChannel; + } //Helpers private static IUser GetAuthor(BaseDiscordClient client, IGuild guild, UserModel model, ulong? webhookId) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs index 397e14e76..321f1f1d2 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestCategoryChannel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -25,10 +25,6 @@ namespace Discord.Rest private string DebuggerDisplay => $"{Name} ({Id}, Category)"; // IGuildChannel - IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) - => throw new NotSupportedException(); - Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) - => throw new NotSupportedException(); Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) => throw new NotSupportedException(); Task> IGuildChannel.GetInvitesAsync(RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs index 04cc5a937..5860d8283 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestChannel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -24,6 +24,8 @@ namespace Discord.Rest case ChannelType.DM: case ChannelType.Group: return CreatePrivate(discord, model) as RestChannel; + case ChannelType.Category: + return RestCategoryChannel.Create(discord, new RestGuild(discord, model.GuildId.Value), model); default: return new RestChannel(discord, model.Id); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs index 026d03cc8..7355e3673 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -16,7 +16,6 @@ namespace Discord.Rest internal IGuild Guild { get; } public string Name { get; private set; } public int Position { get; private set; } - public ulong? CategoryId { get; private set; } public ulong GuildId => Guild.Id; internal RestGuildChannel(BaseDiscordClient discord, IGuild guild, ulong id) @@ -35,7 +34,6 @@ namespace Discord.Rest case ChannelType.Category: return RestCategoryChannel.Create(discord, guild, model); default: - // TODO: Channel categories return new RestGuildChannel(discord, guild, model.Id); } } @@ -64,13 +62,6 @@ namespace Discord.Rest public Task DeleteAsync(RequestOptions options = null) => ChannelHelper.DeleteAsync(this, Discord, options); - public async Task GetCategoryAsync() - { - if (CategoryId.HasValue) - return (await Guild.GetChannelAsync(CategoryId.Value).ConfigureAwait(false)) as ICategoryChannel; - return null; - } - public OverwritePermissions? GetPermissionOverwrite(IUser user) { for (int i = 0; i < _overwrites.Length; i++) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index b01a88210..a08585cd8 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -12,6 +12,7 @@ namespace Discord.Rest public class RestTextChannel : RestGuildChannel, IRestMessageChannel, ITextChannel { public string Topic { get; private set; } + public ulong? CategoryId { get; private set; } public string Mention => MentionUtils.MentionChannel(Id); @@ -31,7 +32,7 @@ namespace Discord.Rest internal override void Update(Model model) { base.Update(model); - + CategoryId = model.CategoryId; Topic = model.Topic.Value; _nsfw = model.Nsfw.GetValueOrDefault(); } @@ -46,7 +47,7 @@ namespace Discord.Rest => ChannelHelper.GetUserAsync(this, Guild, Discord, id, options); public IAsyncEnumerable> GetUsersAsync(RequestOptions options = null) => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); - + public Task GetMessageAsync(ulong id, RequestOptions options = null) => ChannelHelper.GetMessageAsync(this, Discord, id, options); public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) @@ -88,16 +89,19 @@ namespace Discord.Rest => ChannelHelper.GetWebhookAsync(this, Discord, id, options); public Task> GetWebhooksAsync(RequestOptions options = null) => ChannelHelper.GetWebhooksAsync(this, Discord, options); + + public Task GetCategoryAsync(RequestOptions options = null) + => ChannelHelper.GetCategoryAsync(this, Discord, options); private string DebuggerDisplay => $"{Name} ({Id}, Text)"; //ITextChannel async Task ITextChannel.CreateWebhookAsync(string name, Stream avatar, RequestOptions options) - => await CreateWebhookAsync(name, avatar, options); + => await CreateWebhookAsync(name, avatar, options).ConfigureAwait(false); async Task ITextChannel.GetWebhookAsync(ulong id, RequestOptions options) - => await GetWebhookAsync(id, options); + => await GetWebhookAsync(id, options).ConfigureAwait(false); async Task> ITextChannel.GetWebhooksAsync(RequestOptions options) - => await GetWebhooksAsync(options); + => await GetWebhooksAsync(options).ConfigureAwait(false); //IMessageChannel async Task IMessageChannel.GetMessageAsync(ulong id, CacheMode mode, RequestOptions options) @@ -114,6 +118,7 @@ namespace Discord.Rest else return AsyncEnumerable.Empty>(); } + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) @@ -172,5 +177,13 @@ namespace Discord.Rest else return AsyncEnumerable.Empty>(); } + + // INestedChannel + async Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + { + if (CategoryId.HasValue && mode == CacheMode.AllowDownload) + return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel; + return null; + } } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index 300ebd08d..a2bead45f 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using System; using System.Collections.Generic; using System.Diagnostics; @@ -13,6 +13,7 @@ namespace Discord.Rest { public int Bitrate { get; private set; } public int? UserLimit { get; private set; } + public ulong? CategoryId { get; private set; } internal RestVoiceChannel(BaseDiscordClient discord, IGuild guild, ulong id) : base(discord, guild, id) @@ -27,7 +28,7 @@ namespace Discord.Rest internal override void Update(Model model) { base.Update(model); - + CategoryId = model.CategoryId; Bitrate = model.Bitrate.Value; UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } @@ -38,6 +39,9 @@ namespace Discord.Rest Update(model); } + public Task GetCategoryAsync(RequestOptions options = null) + => ChannelHelper.GetCategoryAsync(this, Discord, options); + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; //IAudioChannel @@ -48,5 +52,13 @@ namespace Discord.Rest => Task.FromResult(null); IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => AsyncEnumerable.Empty>(); + + // INestedChannel + async Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + { + if (CategoryId.HasValue && mode == CacheMode.AllowDownload) + return (await Guild.GetChannelAsync(CategoryId.Value, mode, options).ConfigureAwait(false)) as ICategoryChannel; + return null; + } } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs index e7a165c2f..74ca02dba 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketCategoryChannel.cs @@ -20,7 +20,7 @@ namespace Discord.WebSocket ChannelPermission.ViewChannel)).ToImmutableArray(); public IReadOnlyCollection Channels - => Guild.Channels.Where(x => x.CategoryId == Id).ToImmutableArray(); + => Guild.Channels.Where(x => x is INestedChannel nestedChannel && nestedChannel.CategoryId == Id).ToImmutableArray(); internal SocketCategoryChannel(DiscordSocketClient discord, ulong id, SocketGuild guild) : base(discord, id, guild) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs index 2163daf55..bfcffa35f 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGuildChannel.cs @@ -1,4 +1,4 @@ -using Discord.Rest; +using Discord.Rest; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -16,10 +16,7 @@ namespace Discord.WebSocket public SocketGuild Guild { get; } public string Name { get; private set; } - public int Position { get; private set; } - public ulong? CategoryId { get; private set; } - public ICategoryChannel Category - => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; + public int Position { get; private set; } public IReadOnlyCollection PermissionOverwrites => _overwrites; public new virtual IReadOnlyCollection Users => ImmutableArray.Create(); @@ -48,8 +45,7 @@ namespace Discord.WebSocket { Name = model.Name.Value; Position = model.Position.Value; - CategoryId = model.CategoryId; - + var overwrites = model.PermissionOverwrites.Value; var newOverwrites = ImmutableArray.CreateBuilder(overwrites.Length); for (int i = 0; i < overwrites.Length; i++) @@ -135,9 +131,6 @@ namespace Discord.WebSocket IGuild IGuildChannel.Guild => Guild; ulong IGuildChannel.GuildId => Guild.Id; - Task IGuildChannel.GetCategoryAsync() - => Task.FromResult(Category); - async Task> IGuildChannel.GetInvitesAsync(RequestOptions options) => await GetInvitesAsync(options).ConfigureAwait(false); async Task IGuildChannel.CreateInviteAsync(int? maxAge, int? maxUses, bool isTemporary, bool isUnique, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index dc68044fe..1d8041585 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -16,6 +16,9 @@ namespace Discord.WebSocket private readonly MessageCache _messages; public string Topic { get; private set; } + public ulong? CategoryId { get; private set; } + public ICategoryChannel Category + => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; private bool _nsfw; public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); @@ -42,7 +45,7 @@ namespace Discord.WebSocket internal override void Update(ClientState state, Model model) { base.Update(state, model); - + CategoryId = model.CategoryId; Topic = model.Topic.Value; _nsfw = model.Nsfw.GetValueOrDefault(); } @@ -169,5 +172,9 @@ namespace Discord.WebSocket => await SendMessageAsync(text, isTTS, embed, options).ConfigureAwait(false); IDisposable IMessageChannel.EnterTypingState(RequestOptions options) => EnterTypingState(options); + + // INestedChannel + Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(Category); } } diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index e8a669845..349621fac 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using Discord.Rest; using System; using System.Collections.Generic; @@ -15,6 +15,9 @@ namespace Discord.WebSocket { public int Bitrate { get; private set; } public int? UserLimit { get; private set; } + public ulong? CategoryId { get; private set; } + public ICategoryChannel Category + => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; public override IReadOnlyCollection Users => Guild.Users.Where(x => x.VoiceChannel?.Id == Id).ToImmutableArray(); @@ -32,7 +35,7 @@ namespace Discord.WebSocket internal override void Update(ClientState state, Model model) { base.Update(state, model); - + CategoryId = model.CategoryId; Bitrate = model.Bitrate.Value; UserLimit = model.UserLimit.Value != 0 ? model.UserLimit.Value : (int?)null; } @@ -52,7 +55,7 @@ namespace Discord.WebSocket return user; return null; } - + private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; internal new SocketVoiceChannel Clone() => MemberwiseClone() as SocketVoiceChannel; @@ -61,5 +64,9 @@ namespace Discord.WebSocket => Task.FromResult(GetUser(id)); IAsyncEnumerable> IGuildChannel.GetUsersAsync(CacheMode mode, RequestOptions options) => ImmutableArray.Create>(Users).ToAsyncEnumerable(); + + // INestedChannel + Task INestedChannel.GetCategoryAsync(CacheMode mode, RequestOptions options) + => Task.FromResult(Category); } } diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs index cd629faa4..2c189c03f 100644 --- a/test/Discord.Net.Tests/Tests.Channels.cs +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -1,4 +1,5 @@ using Discord.Rest; +using System; using System.Linq; using System.Threading.Tasks; using Xunit; @@ -15,17 +16,28 @@ namespace Discord var text4 = await guild.CreateTextChannelAsync("text4"); var text5 = await guild.CreateTextChannelAsync("text5"); + // create a channel category + var cat1 = await guild.CreateCategoryChannelAsync("cat1"); + + if (text1 == null) + { + // the guild did not have a default channel, so make a new one + text1 = await guild.CreateTextChannelAsync("default"); + } + //Modify #general await text1.ModifyAsync(x => { x.Name = "text1"; x.Position = 1; x.Topic = "Topic1"; + x.CategoryId = cat1.Id; }); await text2.ModifyAsync(x => { x.Position = 2; + x.CategoryId = cat1.Id; }); await text3.ModifyAsync(x => { @@ -89,10 +101,13 @@ namespace Discord var voice2 = await guild.CreateVoiceChannelAsync("voice2"); var voice3 = await guild.CreateVoiceChannelAsync("voice3"); + var cat2 = await guild.CreateCategoryChannelAsync("cat2"); + await voice1.ModifyAsync(x => { x.Bitrate = 96000; x.Position = 1; + x.CategoryId = cat2.Id; }); await voice2.ModifyAsync(x => { @@ -103,6 +118,7 @@ namespace Discord x.Bitrate = 8000; x.Position = 1; x.UserLimit = 16; + x.CategoryId = cat2.Id; }); CheckVoiceChannels(voice1, voice2, voice3); @@ -140,5 +156,63 @@ namespace Discord Assert.Equal(1, voice3.Position); Assert.Equal(16, voice3.UserLimit); } + + [Fact] + public async Task TestChannelCategories() + { + // (await _guild.GetVoiceChannelsAsync()).ToArray() + var channels = await _guild.GetCategoryChannelsAsync(); + + await CheckChannelCategories(channels.ToArray(), (await _guild.GetChannelsAsync()).ToArray()); + } + + private async Task CheckChannelCategories(RestCategoryChannel[] categories, RestGuildChannel[] allChannels) + { + // 2 categories + Assert.Equal(categories.Length, 2); + + var cat1 = categories.Where(x => x.Name == "cat1").FirstOrDefault(); + var cat2 = categories.Where(x => x.Name == "cat2").FirstOrDefault(); + + Assert.NotNull(cat1); + Assert.NotNull(cat2); + + // get text1, text2, ensure they have category id == cat1 + var text1 = allChannels.Where(x => x.Name == "text1").FirstOrDefault() as RestTextChannel; + var text2 = allChannels.Where(x => x.Name == "text2").FirstOrDefault() as RestTextChannel; + + Assert.NotNull(text1); + Assert.NotNull(text2); + + // check that CategoryID and .GetCategoryAsync work correctly + // for both of the text channels + Assert.Equal(text1.CategoryId, cat1.Id); + var text1Cat = await text1.GetCategoryAsync(); + Assert.Equal(text1Cat.Id, cat1.Id); + Assert.Equal(text1Cat.Name, cat1.Name); + + Assert.Equal(text2.CategoryId, cat1.Id); + var text2Cat = await text2.GetCategoryAsync(); + Assert.Equal(text2Cat.Id, cat1.Id); + Assert.Equal(text2Cat.Name, cat1.Name); + + // do the same for the voice channels + var voice1 = allChannels.Where(x => x.Name == "voice1").FirstOrDefault() as RestVoiceChannel; + var voice3 = allChannels.Where(x => x.Name == "voice3").FirstOrDefault() as RestVoiceChannel; + + Assert.NotNull(voice1); + Assert.NotNull(voice3); + + Assert.Equal(voice1.CategoryId, cat2.Id); + var voice1Cat = await voice1.GetCategoryAsync(); + Assert.Equal(voice1Cat.Id, cat2.Id); + Assert.Equal(voice1Cat.Name, cat2.Name); + + Assert.Equal(voice3.CategoryId, cat2.Id); + var voice3Cat = await voice3.GetCategoryAsync(); + Assert.Equal(voice3Cat.Id, cat2.Id); + Assert.Equal(voice3Cat.Name, cat2.Name); + + } } } From a06e21261c66e7aeacde11911ce5ee1ed6baba2f Mon Sep 17 00:00:00 2001 From: Chris Johnston Date: Sat, 26 May 2018 11:15:09 -0700 Subject: [PATCH 33/49] Fix ChannelPermissions Modify parameter to be correct default value (#1003) * fix channel permissions modify parameter to use nullable boolean, correct default value * Add general tests for the ChannelPermissions.Modify method to test default values * remove unused cast in tests * add guildpermission modify no param tests * Add no-param modify tests for OverwritePermissions * fix inconsistent parameters in GuildPermissions cstr * Adjust formatting of methods and cstrs with many parameters * remove temp file that was included. no idea what that is * Fix System dependency I should really stop fixing merge conflicts in the github website. --- .../Permissions/ChannelPermissions.cs | 99 +++++++++--- .../Entities/Permissions/GuildPermissions.cs | 147 ++++++++++++++---- .../Permissions/OverwritePermissions.cs | 76 +++++++-- .../Tests.ChannelPermissions.cs | 21 +++ .../Tests.GuildPermissions.cs | 12 ++ test/Discord.Net.Tests/Tests.Permissions.cs | 66 ++++++++ 6 files changed, 356 insertions(+), 65 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 038b18e2e..fa2dfb576 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -87,12 +87,27 @@ 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? viewChannel = 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? manageRoles = null, bool? manageWebhooks = null) + bool? viewChannel = 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? manageRoles = null, + bool? manageWebhooks = null) { ulong value = initialValue; @@ -121,27 +136,75 @@ 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 viewChannel = 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 manageRoles = false, bool manageWebhooks = false) + bool viewChannel = 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 manageRoles = false, + bool manageWebhooks = false) : this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { } /// 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? viewChannel = 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? manageRoles = null, bool? manageWebhooks = null) - => new ChannelPermissions(RawValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, - embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks); + bool? viewChannel = 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? manageRoles = null, + bool? manageWebhooks = null) + => new ChannelPermissions(RawValue, + createInstantInvite, + manageChannel, + addReactions, + viewChannel, + sendMessages, + sendTTSMessages, + manageMessages, + embedLinks, + attachFiles, + readMessageHistory, + mentionEveryone, + useExternalEmojis, + connect, + speak, + muteMembers, + deafenMembers, + moveMembers, + useVoiceActivation, + manageRoles, + 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 aa914eda1..7704a62d6 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -84,14 +84,35 @@ namespace Discord /// Creates a new GuildPermissions with the provided packed value. public GuildPermissions(ulong rawValue) { RawValue = rawValue; } - private GuildPermissions(ulong initialValue, bool? createInstantInvite = null, bool? kickMembers = null, - bool? banMembers = null, bool? administrator = null, bool? manageChannels = null, bool? manageGuild = null, - bool? addReactions = null, bool? viewAuditLog = null, - bool? viewChannel = 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) + private GuildPermissions(ulong initialValue, + bool? createInstantInvite = null, + bool? kickMembers = null, + bool? banMembers = null, + bool? administrator = null, + bool? manageChannels = null, + bool? manageGuild = null, + bool? addReactions = null, + bool? viewAuditLog = null, + bool? viewChannel = 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) { ulong value = initialValue; @@ -128,32 +149,96 @@ 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 viewAuditLog = false, - bool viewChannel = 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: createInstantInvite, manageRoles: manageRoles, kickMembers: kickMembers, banMembers: banMembers, - administrator: administrator, manageChannels: manageChannels, manageGuild: manageGuild, addReactions: addReactions, - viewAuditLog: viewAuditLog, viewChannel: viewChannel, sendMessages: sendMessages, sendTTSMessages: sendTTSMessages, - manageMessages: manageMessages, embedLinks: embedLinks, attachFiles: attachFiles, readMessageHistory: readMessageHistory, - mentionEveryone: mentionEveryone, useExternalEmojis: useExternalEmojis, connect: connect, speak: speak, muteMembers: muteMembers, - deafenMembers: deafenMembers, moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, changeNickname: changeNickname, - manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, manageEmojis: manageEmojis) + public GuildPermissions( + bool createInstantInvite = false, + bool kickMembers = false, + bool banMembers = false, + bool administrator = false, + bool manageChannels = false, + bool manageGuild = false, + bool addReactions = false, + bool viewAuditLog = false, + bool viewChannel = 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: createInstantInvite, + manageRoles: manageRoles, + kickMembers: kickMembers, + banMembers: banMembers, + administrator: administrator, + manageChannels: manageChannels, + manageGuild: manageGuild, + addReactions: addReactions, + viewAuditLog: viewAuditLog, + viewChannel: viewChannel, + sendMessages: sendMessages, + sendTTSMessages: sendTTSMessages, + manageMessages: manageMessages, + embedLinks: embedLinks, + attachFiles: attachFiles, + readMessageHistory: readMessageHistory, + mentionEveryone: mentionEveryone, + useExternalEmojis: useExternalEmojis, + connect: connect, + speak: speak, + muteMembers: muteMembers, + deafenMembers: deafenMembers, + moveMembers: moveMembers, + useVoiceActivation: useVoiceActivation, + changeNickname: changeNickname, + manageNicknames: manageNicknames, + manageWebhooks: manageWebhooks, + manageEmojis: 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? viewAuditLog = null, - bool? viewChannel = 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) + 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? viewAuditLog = null, + bool? viewChannel = 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, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs index 108b67273..54a4e4035 100644 --- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; @@ -74,12 +74,26 @@ namespace Discord DenyValue = denyValue; } - private OverwritePermissions(ulong allowValue, ulong denyValue, PermValue? createInstantInvite = null, PermValue? manageChannel = null, + private OverwritePermissions(ulong allowValue, ulong denyValue, + PermValue? createInstantInvite = null, + PermValue? manageChannel = null, PermValue? addReactions = null, - PermValue? viewChannel = 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? manageRoles = null, + PermValue? viewChannel = 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? manageRoles = null, PermValue? manageWebhooks = null) { Permissions.SetValue(ref allowValue, ref denyValue, createInstantInvite, ChannelPermission.CreateInstantInvite); @@ -108,23 +122,53 @@ 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 manageRoles = PermValue.Inherit, PermValue manageWebhooks = 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 manageRoles = 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, manageRoles, manageWebhooks) { } /// 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? manageRoles = null, PermValue? manageWebhooks = 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? manageRoles = 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, manageRoles, manageWebhooks); diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs index da969b320..51834e30d 100644 --- a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -22,6 +22,27 @@ namespace Discord var copy = perm.Modify(); Assert.Equal((ulong)0, copy.RawValue); + // test modify with no parameters after using all + copy = ChannelPermissions.Text; + var modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Text.RawValue, modified.RawValue); + + copy = ChannelPermissions.Voice; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Voice.RawValue, modified.RawValue); + + copy = ChannelPermissions.Group; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.Group.RawValue, modified.RawValue); + + copy = ChannelPermissions.DM; + modified = copy.Modify(); // no params should not change the result + Assert.Equal(ChannelPermissions.DM.RawValue, modified.RawValue); + + copy = new ChannelPermissions(useExternalEmojis: true); + modified = copy.Modify(); + Assert.Equal(copy.RawValue, modified.RawValue); + // test the values that are returned by ChannelPermission.All Assert.Equal((ulong)0, ChannelPermissions.None.RawValue); diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs index 2b7c39341..defaf4d02 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -26,6 +26,18 @@ namespace Discord // ensure that the raw values match Assert.Equal((ulong)0, copy.RawValue); + // test modify with no parameters + copy = GuildPermissions.None.Modify(); + Assert.Equal(GuildPermissions.None.RawValue, copy.RawValue); + + // test modify with no paramters on all permissions + copy = GuildPermissions.All.Modify(); + Assert.Equal(GuildPermissions.All.RawValue, copy.RawValue); + + // test modify with no paramters on webhook permissions + copy = GuildPermissions.Webhook.Modify(); + Assert.Equal(GuildPermissions.Webhook.RawValue, copy.RawValue); + // test GuildPermissions.All ulong sumOfAllGuildPermissions = 0; foreach(var v in Enum.GetValues(typeof(GuildPermission))) diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index 0516337da..5a7b5a16b 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -702,5 +702,71 @@ namespace Discord return Task.CompletedTask; } + + /// + /// Tests for the + /// method to ensure that the default no-param call does not modify the resulting value + /// of the OverwritePermissions. + /// + /// + public Task TestOverwritePermissionModifyNoParam() + { + // test for all Text allowed, none denied + var original = new OverwritePermissions(ChannelPermissions.Text.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, text denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Text.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // category allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Category.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, category denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Category.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // DM allowed, none denied + original = new OverwritePermissions(ChannelPermissions.DM.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, DM denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.DM.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // voice allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Voice.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, voice denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Voice.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // group allowed, none denied + original = new OverwritePermissions(ChannelPermissions.Group.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, group denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.Group.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + // none allowed, none denied + original = new OverwritePermissions(ChannelPermissions.None.RawValue, ChannelPermissions.None.RawValue); + Assert.Equal(original.AllowValue, original.Modify().AllowValue); + Assert.Equal(original.DenyValue, original.Modify().DenyValue); + + return Task.CompletedTask; + } } } From 8fb2c71814fad9bcab1888fb8d66d693cc98a4b1 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 27 May 2018 16:37:17 -0400 Subject: [PATCH 34/49] Add new member objects to events --- src/Discord.Net.Rest/API/Common/Message.cs | 8 +++++++- src/Discord.Net.Rest/API/Common/VoiceState.cs | 5 ++++- .../API/Gateway/TypingStartEvent.cs | 6 +++++- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 10 ++++++++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Rest/API/Common/Message.cs b/src/Discord.Net.Rest/API/Common/Message.cs index 9a7629b96..229249ccf 100644 --- a/src/Discord.Net.Rest/API/Common/Message.cs +++ b/src/Discord.Net.Rest/API/Common/Message.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; using System; @@ -12,10 +12,16 @@ namespace Discord.API public MessageType Type { get; set; } [JsonProperty("channel_id")] public ulong ChannelId { get; set; } + // ALWAYS sent on WebSocket messages + [JsonProperty("guild_id")] + public Optional GuildId { get; set; } [JsonProperty("webhook_id")] public Optional WebhookId { get; set; } [JsonProperty("author")] public Optional Author { get; set; } + // ALWAYS sent on WebSocket messages + [JsonProperty("member")] + public Optional Member { get; set; } [JsonProperty("content")] public Optional Content { get; set; } [JsonProperty("timestamp")] diff --git a/src/Discord.Net.Rest/API/Common/VoiceState.cs b/src/Discord.Net.Rest/API/Common/VoiceState.cs index 563a5f95b..b1f937b09 100644 --- a/src/Discord.Net.Rest/API/Common/VoiceState.cs +++ b/src/Discord.Net.Rest/API/Common/VoiceState.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API @@ -11,6 +11,9 @@ namespace Discord.API public ulong? ChannelId { get; set; } [JsonProperty("user_id")] public ulong UserId { get; set; } + // ALWAYS sent over WebSocket, never on REST + [JsonProperty("member")] + public Optional Member { get; set; } [JsonProperty("session_id")] public string SessionId { get; set; } [JsonProperty("deaf")] diff --git a/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs index 3cce512bd..5ceae4b7a 100644 --- a/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs +++ b/src/Discord.Net.WebSocket/API/Gateway/TypingStartEvent.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; namespace Discord.API.Gateway @@ -9,6 +9,10 @@ namespace Discord.API.Gateway public ulong UserId { get; set; } [JsonProperty("channel_id")] public ulong ChannelId { get; set; } + [JsonProperty("guild_id")] + public ulong GuildId { get; set; } + [JsonProperty("member")] + public GuildMember Member { get; set; } [JsonProperty("timestamp")] public int Timestamp { get; set; } } diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 01322a3cc..d66252835 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1091,7 +1091,7 @@ namespace Discord.WebSocket if (author == null) { if (guild != null) - author = guild.AddOrUpdateUser(data.Author.Value); //User has no guild-specific data + author = guild.AddOrUpdateUser(data.Member.Value); //per g250k, we can create an entire member now else if (channel is SocketGroupChannel) author = (channel as SocketGroupChannel).GetOrAddUser(data.Author.Value); else @@ -1361,6 +1361,11 @@ namespace Discord.WebSocket } var user = (channel as SocketChannel).GetUser(data.UserId); + if (user == null) + { + if (guild != null) + user = guild.AddOrUpdateUser(data.Member); + } if (user != null) await TimedInvokeAsync(_userIsTypingEvent, nameof(UserIsTyping), user, channel).ConfigureAwait(false); } @@ -1427,7 +1432,8 @@ namespace Discord.WebSocket user = guild.GetUser(data.UserId); if (user == null) { - await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); + user = guild.AddOrUpdateUser(data.Member.Value); //per g250k, this is always sent + //await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); return; } } From c0c565fd7ee03fa569027fff7f17db90d9376b4f Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 27 May 2018 16:45:54 -0400 Subject: [PATCH 35/49] retain fallback case for if user is still null --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index d66252835..2dccd9321 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1429,11 +1429,10 @@ namespace Discord.WebSocket after = SocketVoiceState.Create(null, data); } - user = guild.GetUser(data.UserId); + user = guild.GetUser(data.UserId) ?? guild.AddOrUpdateUser(data.Member.Value); //per g250k, this is always sent if (user == null) { - user = guild.AddOrUpdateUser(data.Member.Value); //per g250k, this is always sent - //await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); + await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); return; } } From adf4da19fc116759c4246c978143987712fa2116 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 27 May 2018 17:56:14 -0400 Subject: [PATCH 36/49] fix: Properly rethrow exceptions in SocketGuild audio client --- src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 14263f0af..9a9f46baa 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -549,10 +549,10 @@ namespace Discord.WebSocket await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false); } - catch (Exception) + catch (Exception e) { await DisconnectAudioInternalAsync().ConfigureAwait(false); - throw; + throw e; } finally { @@ -566,10 +566,10 @@ namespace Discord.WebSocket throw new TimeoutException(); return await promise.Task.ConfigureAwait(false); } - catch (Exception) + catch (Exception e) { await DisconnectAudioAsync().ConfigureAwait(false); - throw; + throw e; } } From 237ad0f8675023478ec8ad41c7b6b6fd19ceed2d Mon Sep 17 00:00:00 2001 From: Christopher F Date: Sun, 27 May 2018 18:00:51 -0400 Subject: [PATCH 37/49] Revert "fix: Properly rethrow exceptions in SocketGuild audio client" This reverts commit adf4da19fc116759c4246c978143987712fa2116. Someone hasn't written c# in a while --- src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 9a9f46baa..14263f0af 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -549,10 +549,10 @@ namespace Discord.WebSocket await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false); } - catch (Exception e) + catch (Exception) { await DisconnectAudioInternalAsync().ConfigureAwait(false); - throw e; + throw; } finally { @@ -566,10 +566,10 @@ namespace Discord.WebSocket throw new TimeoutException(); return await promise.Task.ConfigureAwait(false); } - catch (Exception e) + catch (Exception) { await DisconnectAudioAsync().ConfigureAwait(false); - throw e; + throw; } } From 683b3f2701087db6d49c3a54c6d99c80ef5b2e5a Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 May 2018 17:12:28 +0100 Subject: [PATCH 38/49] Mark ChannelHelper.IsNsfw Obsolete Discord no longer treats channels prefixed with 'nsfw' as NSFW, so we no longer need to have this check. --- src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 2 ++ src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs | 8 ++++---- .../Entities/Channels/SocketTextChannel.cs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index 4047b7014..fa169a977 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -338,8 +338,10 @@ namespace Discord.Rest return author; } + [Obsolete("Use channel.IsNsfw instead")] public static bool IsNsfw(IChannel channel) => IsNsfw(channel.Name); + [Obsolete("Use Channel.IsNsfw instead")] public static bool IsNsfw(string channelName) => channelName == "nsfw" || channelName.StartsWith("nsfw-"); } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs index a08585cd8..7f57c96ff 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestTextChannel.cs @@ -17,7 +17,7 @@ namespace Discord.Rest public string Mention => MentionUtils.MentionChannel(Id); private bool _nsfw; - public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); + public bool IsNsfw => _nsfw; internal RestTextChannel(BaseDiscordClient discord, IGuild guild, ulong id) : base(discord, guild, id) @@ -47,7 +47,7 @@ namespace Discord.Rest => ChannelHelper.GetUserAsync(this, Guild, Discord, id, options); public IAsyncEnumerable> GetUsersAsync(RequestOptions options = null) => ChannelHelper.GetUsersAsync(this, Guild, Discord, null, null, options); - + public Task GetMessageAsync(ulong id, RequestOptions options = null) => ChannelHelper.GetMessageAsync(this, Discord, id, options); public IAsyncEnumerable> GetMessagesAsync(int limit = DiscordConfig.MaxMessagesPerBatch, RequestOptions options = null) @@ -89,7 +89,7 @@ namespace Discord.Rest => ChannelHelper.GetWebhookAsync(this, Discord, id, options); public Task> GetWebhooksAsync(RequestOptions options = null) => ChannelHelper.GetWebhooksAsync(this, Discord, options); - + public Task GetCategoryAsync(RequestOptions options = null) => ChannelHelper.GetCategoryAsync(this, Discord, options); @@ -118,7 +118,7 @@ namespace Discord.Rest else return AsyncEnumerable.Empty>(); } - + IAsyncEnumerable> IMessageChannel.GetMessagesAsync(ulong fromMessageId, Direction dir, int limit, CacheMode mode, RequestOptions options) { if (mode == CacheMode.AllowDownload) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs index 1d8041585..e41c2ba94 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketTextChannel.cs @@ -21,7 +21,7 @@ namespace Discord.WebSocket => CategoryId.HasValue ? Guild.GetChannel(CategoryId.Value) as ICategoryChannel : null; private bool _nsfw; - public bool IsNsfw => _nsfw || ChannelHelper.IsNsfw(this); + public bool IsNsfw => _nsfw; public string Mention => MentionUtils.MentionChannel(Id); public IReadOnlyCollection CachedMessages => _messages?.Messages ?? ImmutableArray.Create(); From b0042606b628e02ffe2bee3135c7ed42c3b8311f Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 May 2018 17:13:59 +0100 Subject: [PATCH 39/49] Move RpcCategoryChannel into the correct project --- .../Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src => experiment}/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs (100%) diff --git a/src/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs b/experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs similarity index 100% rename from src/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs rename to experiment/Discord.Net.Rpc/Entities/Channels/RpcCategoryChannel.cs From 415e2f773978353025ea8b2f6c3d9929601c28c1 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 May 2018 17:14:18 +0100 Subject: [PATCH 40/49] Fix analyzer warnings with tests Use (actual, expected) format for Assert.Equal, fixes analyzer warnings --- .../Tests.ChannelPermissions.cs | 44 +++++++-------- test/Discord.Net.Tests/Tests.Channels.cs | 4 +- .../Tests.GuildPermissions.cs | 54 +++++++++---------- test/Discord.Net.Tests/Tests.Permissions.cs | 23 ++++---- 4 files changed, 63 insertions(+), 62 deletions(-) diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs index 51834e30d..00306733d 100644 --- a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -82,7 +82,7 @@ namespace Discord ulong dmChannel = (ulong)( ChannelPermission.ViewChannel | ChannelPermission.SendMessages - | ChannelPermission.EmbedLinks + | ChannelPermission.EmbedLinks | ChannelPermission.AttachFiles | ChannelPermission.ReadMessageHistory | ChannelPermission.UseExternalEmojis @@ -118,7 +118,7 @@ namespace Discord // ensure that when modified it works perm = perm.Modify(createInstantInvite: true); Assert.True(perm.CreateInstantInvite); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.CreateInstantInvite); + Assert.Equal((ulong)ChannelPermission.CreateInstantInvite, perm.RawValue); // set false again, move on to next permission perm = perm.Modify(createInstantInvite: false); @@ -130,7 +130,7 @@ namespace Discord perm = perm.Modify(manageChannel: true); Assert.True(perm.ManageChannel); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageChannels); + Assert.Equal((ulong)ChannelPermission.ManageChannels, perm.RawValue); perm = perm.Modify(manageChannel: false); Assert.False(perm.ManageChannel); @@ -141,7 +141,7 @@ namespace Discord perm = perm.Modify(addReactions: true); Assert.True(perm.AddReactions); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.AddReactions); + Assert.Equal((ulong)ChannelPermission.AddReactions, perm.RawValue); perm = perm.Modify(addReactions: false); Assert.False(perm.AddReactions); @@ -152,7 +152,7 @@ namespace Discord perm = perm.Modify(viewChannel: true); Assert.True(perm.ViewChannel); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ViewChannel); + Assert.Equal((ulong)ChannelPermission.ViewChannel, perm.RawValue); perm = perm.Modify(viewChannel: false); Assert.False(perm.ViewChannel); @@ -163,7 +163,7 @@ namespace Discord perm = perm.Modify(sendMessages: true); Assert.True(perm.SendMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.SendMessages); + Assert.Equal((ulong)ChannelPermission.SendMessages, perm.RawValue); perm = perm.Modify(sendMessages: false); Assert.False(perm.SendMessages); @@ -174,7 +174,7 @@ namespace Discord perm = perm.Modify(sendTTSMessages: true); Assert.True(perm.SendTTSMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.SendTTSMessages); + Assert.Equal((ulong)ChannelPermission.SendTTSMessages, perm.RawValue); perm = perm.Modify(sendTTSMessages: false); Assert.False(perm.SendTTSMessages); @@ -185,7 +185,7 @@ namespace Discord perm = perm.Modify(manageMessages: true); Assert.True(perm.ManageMessages); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageMessages); + Assert.Equal((ulong)ChannelPermission.ManageMessages, perm.RawValue); perm = perm.Modify(manageMessages: false); Assert.False(perm.ManageMessages); @@ -196,7 +196,7 @@ namespace Discord perm = perm.Modify(embedLinks: true); Assert.True(perm.EmbedLinks); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.EmbedLinks); + Assert.Equal((ulong)ChannelPermission.EmbedLinks, perm.RawValue); perm = perm.Modify(embedLinks: false); Assert.False(perm.EmbedLinks); @@ -207,7 +207,7 @@ namespace Discord perm = perm.Modify(attachFiles: true); Assert.True(perm.AttachFiles); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.AttachFiles); + Assert.Equal((ulong)ChannelPermission.AttachFiles, perm.RawValue); perm = perm.Modify(attachFiles: false); Assert.False(perm.AttachFiles); @@ -218,7 +218,7 @@ namespace Discord perm = perm.Modify(readMessageHistory: true); Assert.True(perm.ReadMessageHistory); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ReadMessageHistory); + Assert.Equal((ulong)ChannelPermission.ReadMessageHistory, perm.RawValue); perm = perm.Modify(readMessageHistory: false); Assert.False(perm.ReadMessageHistory); @@ -229,7 +229,7 @@ namespace Discord perm = perm.Modify(mentionEveryone: true); Assert.True(perm.MentionEveryone); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MentionEveryone); + Assert.Equal((ulong)ChannelPermission.MentionEveryone, perm.RawValue); perm = perm.Modify(mentionEveryone: false); Assert.False(perm.MentionEveryone); @@ -240,7 +240,7 @@ namespace Discord perm = perm.Modify(useExternalEmojis: true); Assert.True(perm.UseExternalEmojis); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.UseExternalEmojis); + Assert.Equal((ulong)ChannelPermission.UseExternalEmojis, perm.RawValue); perm = perm.Modify(useExternalEmojis: false); Assert.False(perm.UseExternalEmojis); @@ -251,18 +251,18 @@ namespace Discord perm = perm.Modify(connect: true); Assert.True(perm.Connect); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.Connect); + Assert.Equal((ulong)ChannelPermission.Connect, perm.RawValue); perm = perm.Modify(connect: false); Assert.False(perm.Connect); Assert.Equal(ChannelPermissions.None.RawValue, perm.RawValue); - + // individual permission test Assert.False(perm.Speak); perm = perm.Modify(speak: true); Assert.True(perm.Speak); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.Speak); + Assert.Equal((ulong)ChannelPermission.Speak, perm.RawValue); perm = perm.Modify(speak: false); Assert.False(perm.Speak); @@ -273,7 +273,7 @@ namespace Discord perm = perm.Modify(muteMembers: true); Assert.True(perm.MuteMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MuteMembers); + Assert.Equal((ulong)ChannelPermission.MuteMembers, perm.RawValue); perm = perm.Modify(muteMembers: false); Assert.False(perm.MuteMembers); @@ -284,7 +284,7 @@ namespace Discord perm = perm.Modify(deafenMembers: true); Assert.True(perm.DeafenMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.DeafenMembers); + Assert.Equal((ulong)ChannelPermission.DeafenMembers, perm.RawValue); perm = perm.Modify(deafenMembers: false); Assert.False(perm.DeafenMembers); @@ -295,7 +295,7 @@ namespace Discord perm = perm.Modify(moveMembers: true); Assert.True(perm.MoveMembers); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.MoveMembers); + Assert.Equal((ulong)ChannelPermission.MoveMembers, perm.RawValue); perm = perm.Modify(moveMembers: false); Assert.False(perm.MoveMembers); @@ -306,7 +306,7 @@ namespace Discord perm = perm.Modify(useVoiceActivation: true); Assert.True(perm.UseVAD); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.UseVAD); + Assert.Equal((ulong)ChannelPermission.UseVAD, perm.RawValue); perm = perm.Modify(useVoiceActivation: false); Assert.False(perm.UseVAD); @@ -317,7 +317,7 @@ namespace Discord perm = perm.Modify(manageRoles: true); Assert.True(perm.ManageRoles); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageRoles); + Assert.Equal((ulong)ChannelPermission.ManageRoles, perm.RawValue); perm = perm.Modify(manageRoles: false); Assert.False(perm.ManageRoles); @@ -328,7 +328,7 @@ namespace Discord perm = perm.Modify(manageWebhooks: true); Assert.True(perm.ManageWebhooks); - Assert.Equal(perm.RawValue, (ulong)ChannelPermission.ManageWebhooks); + Assert.Equal((ulong)ChannelPermission.ManageWebhooks, perm.RawValue); perm = perm.Modify(manageWebhooks: false); Assert.False(perm.ManageWebhooks); diff --git a/test/Discord.Net.Tests/Tests.Channels.cs b/test/Discord.Net.Tests/Tests.Channels.cs index 2c189c03f..46e28b9da 100644 --- a/test/Discord.Net.Tests/Tests.Channels.cs +++ b/test/Discord.Net.Tests/Tests.Channels.cs @@ -169,7 +169,7 @@ namespace Discord private async Task CheckChannelCategories(RestCategoryChannel[] categories, RestGuildChannel[] allChannels) { // 2 categories - Assert.Equal(categories.Length, 2); + Assert.Equal(2, categories.Length); var cat1 = categories.Where(x => x.Name == "cat1").FirstOrDefault(); var cat2 = categories.Where(x => x.Name == "cat2").FirstOrDefault(); @@ -202,7 +202,7 @@ namespace Discord Assert.NotNull(voice1); Assert.NotNull(voice3); - + Assert.Equal(voice1.CategoryId, cat2.Id); var voice1Cat = await voice1.GetCategoryAsync(); Assert.Equal(voice1Cat.Id, cat2.Id); diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs index defaf4d02..0f813377e 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -49,7 +49,7 @@ namespace Discord Assert.Equal(sumOfAllGuildPermissions, GuildPermissions.All.RawValue); Assert.Equal((ulong)0, GuildPermissions.None.RawValue); - // assert that GuildPermissions.All contains the same number of permissions as the + // assert that GuildPermissions.All contains the same number of permissions as the // GuildPermissions enum Assert.Equal(Enum.GetValues(typeof(GuildPermission)).Length, GuildPermissions.All.ToList().Count); @@ -76,7 +76,7 @@ namespace Discord // ensure that when we modify it the parameter works perm = perm.Modify(createInstantInvite: true); Assert.True(perm.CreateInstantInvite); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.CreateInstantInvite); + Assert.Equal((ulong)GuildPermission.CreateInstantInvite, perm.RawValue); // set it false again, then move on to the next permission perm = perm.Modify(createInstantInvite: false); @@ -86,7 +86,7 @@ namespace Discord // individual permission test perm = perm.Modify(kickMembers: true); Assert.True(perm.KickMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.KickMembers); + Assert.Equal((ulong)GuildPermission.KickMembers, perm.RawValue); perm = perm.Modify(kickMembers: false); Assert.False(perm.KickMembers); @@ -95,7 +95,7 @@ namespace Discord // individual permission test perm = perm.Modify(banMembers: true); Assert.True(perm.BanMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.BanMembers); + Assert.Equal((ulong)GuildPermission.BanMembers, perm.RawValue); perm = perm.Modify(banMembers: false); Assert.False(perm.BanMembers); @@ -104,7 +104,7 @@ namespace Discord // individual permission test perm = perm.Modify(administrator: true); Assert.True(perm.Administrator); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Administrator); + Assert.Equal((ulong)GuildPermission.Administrator, perm.RawValue); perm = perm.Modify(administrator: false); Assert.False(perm.Administrator); @@ -113,7 +113,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageChannels: true); Assert.True(perm.ManageChannels); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageChannels); + Assert.Equal((ulong)GuildPermission.ManageChannels, perm.RawValue); perm = perm.Modify(manageChannels: false); Assert.False(perm.ManageChannels); @@ -122,7 +122,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageGuild: true); Assert.True(perm.ManageGuild); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageGuild); + Assert.Equal((ulong)GuildPermission.ManageGuild, perm.RawValue); perm = perm.Modify(manageGuild: false); Assert.False(perm.ManageGuild); @@ -132,7 +132,7 @@ namespace Discord // individual permission test perm = perm.Modify(addReactions: true); Assert.True(perm.AddReactions); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.AddReactions); + Assert.Equal((ulong)GuildPermission.AddReactions, perm.RawValue); perm = perm.Modify(addReactions: false); Assert.False(perm.AddReactions); @@ -142,7 +142,7 @@ namespace Discord // individual permission test perm = perm.Modify(viewAuditLog: true); Assert.True(perm.ViewAuditLog); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewAuditLog); + Assert.Equal((ulong)GuildPermission.ViewAuditLog, perm.RawValue); perm = perm.Modify(viewAuditLog: false); Assert.False(perm.ViewAuditLog); @@ -152,7 +152,7 @@ namespace Discord // individual permission test perm = perm.Modify(viewChannel: true); Assert.True(perm.ViewChannel); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ViewChannel); + Assert.Equal((ulong)GuildPermission.ViewChannel, perm.RawValue); perm = perm.Modify(viewChannel: false); Assert.False(perm.ViewChannel); @@ -162,7 +162,7 @@ namespace Discord // individual permission test perm = perm.Modify(sendMessages: true); Assert.True(perm.SendMessages); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.SendMessages); + Assert.Equal((ulong)GuildPermission.SendMessages, perm.RawValue); perm = perm.Modify(sendMessages: false); Assert.False(perm.SendMessages); @@ -171,7 +171,7 @@ namespace Discord // individual permission test perm = perm.Modify(embedLinks: true); Assert.True(perm.EmbedLinks); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.EmbedLinks); + Assert.Equal((ulong)GuildPermission.EmbedLinks, perm.RawValue); perm = perm.Modify(embedLinks: false); Assert.False(perm.EmbedLinks); @@ -180,7 +180,7 @@ namespace Discord // individual permission test perm = perm.Modify(attachFiles: true); Assert.True(perm.AttachFiles); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.AttachFiles); + Assert.Equal((ulong)GuildPermission.AttachFiles, perm.RawValue); perm = perm.Modify(attachFiles: false); Assert.False(perm.AttachFiles); @@ -189,7 +189,7 @@ namespace Discord // individual permission test perm = perm.Modify(readMessageHistory: true); Assert.True(perm.ReadMessageHistory); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ReadMessageHistory); + Assert.Equal((ulong)GuildPermission.ReadMessageHistory, perm.RawValue); perm = perm.Modify(readMessageHistory: false); Assert.False(perm.ReadMessageHistory); @@ -198,7 +198,7 @@ namespace Discord // individual permission test perm = perm.Modify(mentionEveryone: true); Assert.True(perm.MentionEveryone); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MentionEveryone); + Assert.Equal((ulong)GuildPermission.MentionEveryone, perm.RawValue); perm = perm.Modify(mentionEveryone: false); Assert.False(perm.MentionEveryone); @@ -207,7 +207,7 @@ namespace Discord // individual permission test perm = perm.Modify(useExternalEmojis: true); Assert.True(perm.UseExternalEmojis); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseExternalEmojis); + Assert.Equal((ulong)GuildPermission.UseExternalEmojis, perm.RawValue); perm = perm.Modify(useExternalEmojis: false); Assert.False(perm.UseExternalEmojis); @@ -216,7 +216,7 @@ namespace Discord // individual permission test perm = perm.Modify(connect: true); Assert.True(perm.Connect); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Connect); + Assert.Equal((ulong)GuildPermission.Connect, perm.RawValue); perm = perm.Modify(connect: false); Assert.False(perm.Connect); @@ -225,7 +225,7 @@ namespace Discord // individual permission test perm = perm.Modify(speak: true); Assert.True(perm.Speak); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.Speak); + Assert.Equal((ulong)GuildPermission.Speak, perm.RawValue); perm = perm.Modify(speak: false); Assert.False(perm.Speak); @@ -234,7 +234,7 @@ namespace Discord // individual permission test perm = perm.Modify(muteMembers: true); Assert.True(perm.MuteMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MuteMembers); + Assert.Equal((ulong)GuildPermission.MuteMembers, perm.RawValue); perm = perm.Modify(muteMembers: false); Assert.False(perm.MuteMembers); @@ -243,7 +243,7 @@ namespace Discord // individual permission test perm = perm.Modify(deafenMembers: true); Assert.True(perm.DeafenMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.DeafenMembers); + Assert.Equal((ulong)GuildPermission.DeafenMembers, perm.RawValue); perm = perm.Modify(deafenMembers: false); Assert.False(perm.DeafenMembers); @@ -252,7 +252,7 @@ namespace Discord // individual permission test perm = perm.Modify(moveMembers: true); Assert.True(perm.MoveMembers); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.MoveMembers); + Assert.Equal((ulong)GuildPermission.MoveMembers, perm.RawValue); perm = perm.Modify(moveMembers: false); Assert.False(perm.MoveMembers); @@ -261,7 +261,7 @@ namespace Discord // individual permission test perm = perm.Modify(useVoiceActivation: true); Assert.True(perm.UseVAD); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.UseVAD); + Assert.Equal((ulong)GuildPermission.UseVAD, perm.RawValue); perm = perm.Modify(useVoiceActivation: false); Assert.False(perm.UseVAD); @@ -270,7 +270,7 @@ namespace Discord // individual permission test perm = perm.Modify(changeNickname: true); Assert.True(perm.ChangeNickname); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ChangeNickname); + Assert.Equal((ulong)GuildPermission.ChangeNickname, perm.RawValue); perm = perm.Modify(changeNickname: false); Assert.False(perm.ChangeNickname); @@ -279,7 +279,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageNicknames: true); Assert.True(perm.ManageNicknames); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageNicknames); + Assert.Equal((ulong)GuildPermission.ManageNicknames, perm.RawValue); perm = perm.Modify(manageNicknames: false); Assert.False(perm.ManageNicknames); @@ -288,7 +288,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageRoles: true); Assert.True(perm.ManageRoles); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageRoles); + Assert.Equal((ulong)GuildPermission.ManageRoles, perm.RawValue); perm = perm.Modify(manageRoles: false); Assert.False(perm.ManageRoles); @@ -297,7 +297,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageWebhooks: true); Assert.True(perm.ManageWebhooks); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageWebhooks); + Assert.Equal((ulong)GuildPermission.ManageWebhooks, perm.RawValue); perm = perm.Modify(manageWebhooks: false); Assert.False(perm.ManageWebhooks); @@ -306,7 +306,7 @@ namespace Discord // individual permission test perm = perm.Modify(manageEmojis: true); Assert.True(perm.ManageEmojis); - Assert.Equal(perm.RawValue, (ulong)GuildPermission.ManageEmojis); + Assert.Equal((ulong)GuildPermission.ManageEmojis, perm.RawValue); perm = perm.Modify(manageEmojis: false); Assert.False(perm.ManageEmojis); diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index 5a7b5a16b..f87522105 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -77,7 +77,7 @@ namespace Discord /// /// Tests for the class. - /// + /// /// Tests that text channel permissions get the right value /// from the Has method. /// @@ -114,7 +114,7 @@ namespace Discord /// /// Tests for the class. - /// + /// /// Tests that no channel permissions get the right value /// from the Has method. /// @@ -151,7 +151,7 @@ namespace Discord /// /// Tests for the class. - /// + /// /// Tests that the dm channel permissions get the right value /// from the Has method. /// @@ -188,7 +188,7 @@ namespace Discord /// /// Tests for the class. - /// + /// /// Tests that the group channel permissions get the right value /// from the Has method. /// @@ -225,7 +225,7 @@ namespace Discord /// /// Tests for the class. - /// + /// /// Tests that the voice channel permissions get the right value /// from the Has method. /// @@ -262,8 +262,8 @@ namespace Discord /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when no permissions are set. /// /// @@ -305,8 +305,8 @@ namespace Discord /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when all permissions are set. /// /// @@ -349,8 +349,8 @@ namespace Discord /// /// Tests for the class. - /// - /// Test that that the Has method of + /// + /// Test that that the Has method of /// returns the correct value when webhook permissions are set. /// /// @@ -709,6 +709,7 @@ namespace Discord /// of the OverwritePermissions. /// /// + [Fact] public Task TestOverwritePermissionModifyNoParam() { // test for all Text allowed, none denied From 0c7cb73b06aba3eaeb811b4671fd0f4a5078a491 Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 May 2018 17:41:05 +0100 Subject: [PATCH 41/49] Fix permission-related ViewChannel tests --- test/Discord.Net.Tests/Tests.ChannelPermissions.cs | 1 + test/Discord.Net.Tests/Tests.GuildPermissions.cs | 9 +++++++-- test/Discord.Net.Tests/Tests.Permissions.cs | 8 +++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs index 00306733d..dd87c2e24 100644 --- a/test/Discord.Net.Tests/Tests.ChannelPermissions.cs +++ b/test/Discord.Net.Tests/Tests.ChannelPermissions.cs @@ -68,6 +68,7 @@ namespace Discord ulong voiceChannel = (ulong)( ChannelPermission.CreateInstantInvite | ChannelPermission.ManageChannels + | ChannelPermission.ViewChannel | ChannelPermission.Connect | ChannelPermission.Speak | ChannelPermission.MuteMembers diff --git a/test/Discord.Net.Tests/Tests.GuildPermissions.cs b/test/Discord.Net.Tests/Tests.GuildPermissions.cs index 0f813377e..bbd6621b5 100644 --- a/test/Discord.Net.Tests/Tests.GuildPermissions.cs +++ b/test/Discord.Net.Tests/Tests.GuildPermissions.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -38,9 +39,13 @@ namespace Discord copy = GuildPermissions.Webhook.Modify(); Assert.Equal(GuildPermissions.Webhook.RawValue, copy.RawValue); + // Get all distinct values (ReadMessages = ViewChannel) + var enumValues = (Enum.GetValues(typeof(GuildPermission)) as GuildPermission[]) + .Distinct() + .ToArray(); // test GuildPermissions.All ulong sumOfAllGuildPermissions = 0; - foreach(var v in Enum.GetValues(typeof(GuildPermission))) + foreach(var v in enumValues) { sumOfAllGuildPermissions |= (ulong)v; } @@ -51,7 +56,7 @@ namespace Discord // assert that GuildPermissions.All contains the same number of permissions as the // GuildPermissions enum - Assert.Equal(Enum.GetValues(typeof(GuildPermission)).Length, GuildPermissions.All.ToList().Count); + Assert.Equal(enumValues.Length, GuildPermissions.All.ToList().Count); // assert that webhook has the same raw value ulong webHookPermissions = (ulong)( diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index f87522105..258e2995d 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading.Tasks; using Xunit; @@ -21,6 +22,7 @@ namespace Discord /// private void TestHelper(ulong rawValue, ulong flagValue, bool expected) { + Debug.Print($"Expecting {expected} for {rawValue} w/ {flagValue}"); Assert.Equal(expected, Permissions.GetValue(rawValue, flagValue)); // check that toggling the bit works @@ -239,7 +241,7 @@ namespace Discord TestHelper(value, ChannelPermission.CreateInstantInvite, true); TestHelper(value, ChannelPermission.ManageChannels, true); TestHelper(value, ChannelPermission.AddReactions, false); - TestHelper(value, ChannelPermission.ViewChannel, false); + TestHelper(value, ChannelPermission.ViewChannel, true); TestHelper(value, ChannelPermission.SendMessages, false); TestHelper(value, ChannelPermission.SendTTSMessages, false); TestHelper(value, ChannelPermission.ManageMessages, false); @@ -659,7 +661,7 @@ namespace Discord TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Allow); TestHelper(value, ChannelPermission.ManageChannels, PermValue.Allow); TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); - TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); + TestHelper(value, ChannelPermission.ViewChannel, PermValue.Allow); TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); @@ -682,7 +684,7 @@ namespace Discord TestHelper(value, ChannelPermission.CreateInstantInvite, PermValue.Deny); TestHelper(value, ChannelPermission.ManageChannels, PermValue.Deny); TestHelper(value, ChannelPermission.AddReactions, PermValue.Inherit); - TestHelper(value, ChannelPermission.ViewChannel, PermValue.Inherit); + TestHelper(value, ChannelPermission.ViewChannel, PermValue.Deny); TestHelper(value, ChannelPermission.SendMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.SendTTSMessages, PermValue.Inherit); TestHelper(value, ChannelPermission.ManageMessages, PermValue.Inherit); From a718a7d3c234a37d1c792ac84af0ae8dce61c1a5 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 28 May 2018 12:45:49 -0400 Subject: [PATCH 42/49] cleanup: Remove ChannelHelper#IsNsfw Closes #1074 This code was not referenced anywhere in the active codebase. --- src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs index fa169a977..50cb352a7 100644 --- a/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs +++ b/src/Discord.Net.Rest/Entities/Channels/ChannelHelper.cs @@ -337,12 +337,5 @@ namespace Discord.Rest author = RestUser.Create(client, guild, model, webhookId); return author; } - - [Obsolete("Use channel.IsNsfw instead")] - public static bool IsNsfw(IChannel channel) - => IsNsfw(channel.Name); - [Obsolete("Use Channel.IsNsfw instead")] - public static bool IsNsfw(string channelName) => - channelName == "nsfw" || channelName.StartsWith("nsfw-"); } } From aeb30955932803e88fcd056f817f449e8664715f Mon Sep 17 00:00:00 2001 From: FiniteReality Date: Mon, 28 May 2018 17:48:38 +0100 Subject: [PATCH 43/49] Remove Debug.Print call causing tests to fail Forgot to hit save properly... Whoops. --- test/Discord.Net.Tests/Tests.Permissions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/Discord.Net.Tests/Tests.Permissions.cs b/test/Discord.Net.Tests/Tests.Permissions.cs index 258e2995d..2f72f272d 100644 --- a/test/Discord.Net.Tests/Tests.Permissions.cs +++ b/test/Discord.Net.Tests/Tests.Permissions.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Threading.Tasks; using Xunit; @@ -22,7 +21,6 @@ namespace Discord /// private void TestHelper(ulong rawValue, ulong flagValue, bool expected) { - Debug.Print($"Expecting {expected} for {rawValue} w/ {flagValue}"); Assert.Equal(expected, Permissions.GetValue(rawValue, flagValue)); // check that toggling the bit works From 64d8938ed5100d4c8f3a2afaec3a6becb53cf9b8 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 28 May 2018 15:25:46 -0400 Subject: [PATCH 44/49] fix: Discord does not always send the member on VOICE_STATE_UPDATE some guilds aren't on g250k yet? not sure --- src/Discord.Net.WebSocket/DiscordSocketClient.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.WebSocket/DiscordSocketClient.cs b/src/Discord.Net.WebSocket/DiscordSocketClient.cs index 2dccd9321..793d2b29c 100644 --- a/src/Discord.Net.WebSocket/DiscordSocketClient.cs +++ b/src/Discord.Net.WebSocket/DiscordSocketClient.cs @@ -1429,7 +1429,9 @@ namespace Discord.WebSocket after = SocketVoiceState.Create(null, data); } - user = guild.GetUser(data.UserId) ?? guild.AddOrUpdateUser(data.Member.Value); //per g250k, this is always sent + // per g250k, this should always be sent, but apparently not always + user = guild.GetUser(data.UserId) + ?? (data.Member.IsSpecified ? guild.AddOrUpdateUser(data.Member.Value) : null); if (user == null) { await UnknownGuildUserAsync(type, data.UserId, guild.Id).ConfigureAwait(false); From 9ba38d77966897037cb6355eda2215fd9a235712 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 28 May 2018 15:35:59 -0400 Subject: [PATCH 45/49] api: upgrade audio client to VoiceWS v3 --- .../API/Voice/HelloEvent.cs | 10 ++++++++++ .../API/Voice/ReadyEvent.cs | 4 +++- .../API/Voice/VoiceOpCode.cs | 16 ++++++++++++---- src/Discord.Net.WebSocket/Audio/AudioClient.cs | 14 ++++++++++---- 4 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs diff --git a/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs b/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs new file mode 100644 index 000000000..8fdb0808f --- /dev/null +++ b/src/Discord.Net.WebSocket/API/Voice/HelloEvent.cs @@ -0,0 +1,10 @@ +using Newtonsoft.Json; + +namespace Discord.API.Voice +{ + internal class HelloEvent + { + [JsonProperty("heartbeat_interval")] + public int HeartbeatInterval { get; set; } + } +} diff --git a/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs index 2a134ced1..7188cd8f7 100644 --- a/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs +++ b/src/Discord.Net.WebSocket/API/Voice/ReadyEvent.cs @@ -1,5 +1,6 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 using Newtonsoft.Json; +using System; namespace Discord.API.Voice { @@ -14,6 +15,7 @@ namespace Discord.API.Voice [JsonProperty("modes")] public string[] Modes { get; set; } [JsonProperty("heartbeat_interval")] + [Obsolete("This field is errorneous and should not be used", true)] public int HeartbeatInterval { get; set; } } } diff --git a/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs index ae11a4c8f..67afe6173 100644 --- a/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs +++ b/src/Discord.Net.WebSocket/API/Voice/VoiceOpCode.cs @@ -1,4 +1,4 @@ -#pragma warning disable CS1591 +#pragma warning disable CS1591 namespace Discord.API.Voice { internal enum VoiceOpCode : byte @@ -11,11 +11,19 @@ namespace Discord.API.Voice Ready = 2, /// C→S - Used to keep the connection alive and measure latency. Heartbeat = 3, - /// C←S - Used to reply to a client's heartbeat. - HeartbeatAck = 3, /// C←S - Used to provide an encryption key to the client. SessionDescription = 4, /// C↔S - Used to inform that a certain user is speaking. - Speaking = 5 + Speaking = 5, + /// C←S - Used to reply to a client's heartbeat. + HeartbeatAck = 6, + /// C→S - Used to resume a connection. + Resume = 7, + /// C←S - Used to inform the client the heartbeat interval. + Hello = 8, + /// C←S - Used to acknowledge a resumed connection. + Resumed = 9, + /// C←S - Used to notify that a client has disconnected. + ClientDisconnect = 13, } } diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 1f33b3cc5..4e10d0c54 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -1,4 +1,4 @@ -using Discord.API.Voice; +using Discord.API.Voice; using Discord.Audio.Streams; using Discord.Logging; using Discord.Net.Converters; @@ -107,7 +107,7 @@ namespace Discord.Audio private async Task OnConnectingAsync() { await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); - await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false); + await ApiClient.ConnectAsync("wss://" + _url + "?v=3").ConfigureAwait(false); await _audioLogger.DebugAsync("Listening on port " + ApiClient.UdpPort).ConfigureAwait(false); await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); @@ -216,6 +216,14 @@ namespace Discord.Audio { switch (opCode) { + case VoiceOpCode.Hello: + { + await _audioLogger.DebugAsync("Received Hello").ConfigureAwait(false); + var data = (payload as JToken).ToObject(_serializer); + + _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); + } + break; case VoiceOpCode.Ready: { await _audioLogger.DebugAsync("Received Ready").ConfigureAwait(false); @@ -225,8 +233,6 @@ namespace Discord.Audio if (!data.Modes.Contains(DiscordVoiceAPIClient.Mode)) throw new InvalidOperationException($"Discord does not support {DiscordVoiceAPIClient.Mode}"); - - _heartbeatTask = RunHeartbeatAsync(data.HeartbeatInterval, _connection.CancelToken); ApiClient.SetUdpEndpoint(data.Ip, data.Port); await ApiClient.SendDiscoveryAsync(_ssrc).ConfigureAwait(false); From 513a489bf82ed4e845ff3df9fe21c06e80bb28b8 Mon Sep 17 00:00:00 2001 From: Christopher F Date: Mon, 28 May 2018 15:45:42 -0400 Subject: [PATCH 46/49] codefix: add VoiceAPIVersion constant to DiscordConfig --- src/Discord.Net.Core/DiscordConfig.cs | 1 + src/Discord.Net.WebSocket/Audio/AudioClient.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Discord.Net.Core/DiscordConfig.cs b/src/Discord.Net.Core/DiscordConfig.cs index 5cdf6a77e..8c6cfc28c 100644 --- a/src/Discord.Net.Core/DiscordConfig.cs +++ b/src/Discord.Net.Core/DiscordConfig.cs @@ -5,6 +5,7 @@ namespace Discord public class DiscordConfig { public const int APIVersion = 6; + public const int VoiceAPIVersion = 3; public static string Version { get; } = typeof(DiscordConfig).GetTypeInfo().Assembly.GetCustomAttribute()?.InformationalVersion ?? typeof(DiscordConfig).GetTypeInfo().Assembly.GetName().Version.ToString(3) ?? diff --git a/src/Discord.Net.WebSocket/Audio/AudioClient.cs b/src/Discord.Net.WebSocket/Audio/AudioClient.cs index 4e10d0c54..fb97c1dc9 100644 --- a/src/Discord.Net.WebSocket/Audio/AudioClient.cs +++ b/src/Discord.Net.WebSocket/Audio/AudioClient.cs @@ -107,7 +107,7 @@ namespace Discord.Audio private async Task OnConnectingAsync() { await _audioLogger.DebugAsync("Connecting ApiClient").ConfigureAwait(false); - await ApiClient.ConnectAsync("wss://" + _url + "?v=3").ConfigureAwait(false); + await ApiClient.ConnectAsync("wss://" + _url + "?v=" + DiscordConfig.VoiceAPIVersion).ConfigureAwait(false); await _audioLogger.DebugAsync("Listening on port " + ApiClient.UdpPort).ConfigureAwait(false); await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false); await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false); From 3acf2a9a6bb13d5b75c09babaf7234cb2707f4e8 Mon Sep 17 00:00:00 2001 From: HelpfulStranger999 Date: Mon, 28 May 2018 14:59:30 -0500 Subject: [PATCH 47/49] Refactors readMessages, fixing from #1033 (#1075) --- .../Entities/Permissions/OverwritePermissions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs index 54a4e4035..b8b4b83e2 100644 --- a/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/OverwritePermissions.cs @@ -126,7 +126,7 @@ namespace Discord PermValue createInstantInvite = PermValue.Inherit, PermValue manageChannel = PermValue.Inherit, PermValue addReactions = PermValue.Inherit, - PermValue readMessages = PermValue.Inherit, + PermValue viewChannel = PermValue.Inherit, PermValue sendMessages = PermValue.Inherit, PermValue sendTTSMessages = PermValue.Inherit, PermValue manageMessages = PermValue.Inherit, @@ -143,7 +143,7 @@ namespace Discord PermValue useVoiceActivation = PermValue.Inherit, PermValue manageRoles = PermValue.Inherit, PermValue manageWebhooks = PermValue.Inherit) - : this(0, 0, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, + : this(0, 0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks) { } @@ -152,7 +152,7 @@ namespace Discord PermValue? createInstantInvite = null, PermValue? manageChannel = null, PermValue? addReactions = null, - PermValue? readMessages = null, + PermValue? viewChannel = null, PermValue? sendMessages = null, PermValue? sendTTSMessages = null, PermValue? manageMessages = null, @@ -169,7 +169,7 @@ namespace Discord PermValue? useVoiceActivation = null, PermValue? manageRoles = null, PermValue? manageWebhooks = null) - => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, readMessages, sendMessages, sendTTSMessages, manageMessages, + => new OverwritePermissions(AllowValue, DenyValue, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, manageRoles, manageWebhooks); From 890904f32cf6c38abea60149b858e2bf8f63154a Mon Sep 17 00:00:00 2001 From: NovusTheory Date: Thu, 31 May 2018 16:21:57 -0500 Subject: [PATCH 48/49] Allow external VoiceChannel client API implementation (#1057) * ConnectAsync(bool external) API implemented This allows developers to handle the AudioClient externally (for example with Lavalink) * Modify ConnectAsync API and add DisconnectAsync to IAudioChannel * Update summary message on IAudioChannel.DisconnectAsync() * Review changes: - Fix `if (!external)` styling - Remove unecessary ConnectAsync overload * SocketVoiceChannel overload update * Update IAudioChannel.ConnectAsync() - Remove ConfigAction from parameters - Add SelfDeafen/SelfMute to parameters * Remove SocketVoiceChannel.ConnectAsync() default params (Inherit) --- .../Entities/Channels/IAudioChannel.cs | 7 ++++-- .../Entities/Channels/RestGroupChannel.cs | 3 ++- .../Entities/Channels/RestVoiceChannel.cs | 3 ++- .../Entities/Channels/SocketGroupChannel.cs | 3 ++- .../Entities/Channels/SocketVoiceChannel.cs | 7 ++++-- .../Entities/Guilds/SocketGuild.cs | 24 ++++++++++++------- 6 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs index afb81d92f..a152ff744 100644 --- a/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs +++ b/src/Discord.Net.Core/Entities/Channels/IAudioChannel.cs @@ -1,4 +1,4 @@ -using Discord.Audio; +using Discord.Audio; using System; using System.Threading.Tasks; @@ -7,6 +7,9 @@ namespace Discord public interface IAudioChannel : IChannel { /// Connects to this audio channel. - Task ConnectAsync(Action configAction = null); + Task ConnectAsync(bool selfDeaf = false, bool selfMute = false, bool external = false); + + /// Disconnects from this audio channel. + Task DisconnectAsync(); } } diff --git a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs index 367abfb4a..5ac0f2c40 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs @@ -147,7 +147,8 @@ namespace Discord.Rest => EnterTypingState(options); //IAudioChannel - Task IAudioChannel.ConnectAsync(Action configAction) { throw new NotSupportedException(); } + Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); } + Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); } //IChannel Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs index a2bead45f..13f3b5efa 100644 --- a/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs +++ b/src/Discord.Net.Rest/Entities/Channels/RestVoiceChannel.cs @@ -45,7 +45,8 @@ namespace Discord.Rest private string DebuggerDisplay => $"{Name} ({Id}, Voice)"; //IAudioChannel - Task IAudioChannel.ConnectAsync(Action configAction) { throw new NotSupportedException(); } + Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); } + Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); } //IGuildChannel Task IGuildChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs index 81b5cfbf3..125625456 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs @@ -211,7 +211,8 @@ namespace Discord.WebSocket => EnterTypingState(options); //IAudioChannel - Task IAudioChannel.ConnectAsync(Action configAction) { throw new NotSupportedException(); } + Task IAudioChannel.ConnectAsync(bool selfDeaf, bool selfMute, bool external) { throw new NotSupportedException(); } + Task IAudioChannel.DisconnectAsync() { throw new NotSupportedException(); } //IChannel Task IChannel.GetUserAsync(ulong id, CacheMode mode, RequestOptions options) diff --git a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs index 349621fac..f967e6e6a 100644 --- a/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs +++ b/src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs @@ -43,11 +43,14 @@ namespace Discord.WebSocket public Task ModifyAsync(Action func, RequestOptions options = null) => ChannelHelper.ModifyAsync(this, Discord, func, options); - public async Task ConnectAsync(Action configAction = null) + public async Task ConnectAsync(bool selfDeaf, bool selfMute, bool external) { - return await Guild.ConnectAudioAsync(Id, false, false, configAction).ConfigureAwait(false); + return await Guild.ConnectAudioAsync(Id, selfDeaf, selfMute, external).ConfigureAwait(false); } + public async Task DisconnectAsync() + => await Guild.DisconnectAudioAsync(); + public override SocketGuildUser GetUser(ulong id) { var user = Guild.GetUser(id); diff --git a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs index 14263f0af..f41f61a7c 100644 --- a/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs +++ b/src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs @@ -508,11 +508,8 @@ namespace Discord.WebSocket { return _audioClient?.GetInputStream(userId); } - internal async Task ConnectAudioAsync(ulong channelId, bool selfDeaf, bool selfMute, Action configAction) + internal async Task ConnectAudioAsync(ulong channelId, bool selfDeaf, bool selfMute, bool external) { - selfDeaf = false; - selfMute = false; - TaskCompletionSource promise; await _audioLock.WaitAsync().ConfigureAwait(false); @@ -522,6 +519,13 @@ namespace Discord.WebSocket promise = new TaskCompletionSource(); _audioConnectPromise = promise; + if (external) + { + var _ = promise.TrySetResultAsync(null); + await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false); + return null; + } + if (_audioClient == null) { var audioClient = new AudioClient(this, Discord.GetAudioId(), channelId); @@ -529,7 +533,9 @@ namespace Discord.WebSocket { if (!promise.Task.IsCompleted) { - try { audioClient.Dispose(); } catch { } + try + { audioClient.Dispose(); } + catch { } _audioClient = null; if (ex != null) await promise.TrySetExceptionAsync(ex); @@ -543,7 +549,6 @@ namespace Discord.WebSocket var _ = promise.TrySetResultAsync(_audioClient); return Task.Delay(0); }; - configAction?.Invoke(audioClient); _audioClient = audioClient; } @@ -602,8 +607,11 @@ namespace Discord.WebSocket await _audioLock.WaitAsync().ConfigureAwait(false); try { - await RepopulateAudioStreamsAsync().ConfigureAwait(false); - await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); + if (_audioClient != null) + { + await RepopulateAudioStreamsAsync().ConfigureAwait(false); + await _audioClient.StartAsync(url, Discord.CurrentUser.Id, voiceState.VoiceSessionId, token).ConfigureAwait(false); + } } catch (OperationCanceledException) { From 48fed064d446ca4a1c685893320e09b638eace89 Mon Sep 17 00:00:00 2001 From: NovusTheory Date: Sat, 16 Jun 2018 22:34:12 -0500 Subject: [PATCH 49/49] Add missing VoiceServerUpdated event (#1089) --- src/Discord.Net.WebSocket/DiscordShardedClient.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Discord.Net.WebSocket/DiscordShardedClient.cs b/src/Discord.Net.WebSocket/DiscordShardedClient.cs index dad53b3b7..26d9363eb 100644 --- a/src/Discord.Net.WebSocket/DiscordShardedClient.cs +++ b/src/Discord.Net.WebSocket/DiscordShardedClient.cs @@ -309,6 +309,7 @@ namespace Discord.WebSocket client.UserUpdated += (oldUser, newUser) => _userUpdatedEvent.InvokeAsync(oldUser, newUser); client.GuildMemberUpdated += (oldUser, newUser) => _guildMemberUpdatedEvent.InvokeAsync(oldUser, newUser); client.UserVoiceStateUpdated += (user, oldVoiceState, newVoiceState) => _userVoiceStateUpdatedEvent.InvokeAsync(user, oldVoiceState, newVoiceState); + client.VoiceServerUpdated += (server) => _voiceServerUpdatedEvent.InvokeAsync(server); client.CurrentUserUpdated += (oldUser, newUser) => _selfUpdatedEvent.InvokeAsync(oldUser, newUser); client.UserIsTyping += (oldUser, newUser) => _userIsTypingEvent.InvokeAsync(oldUser, newUser); client.RecipientAdded += (user) => _recipientAddedEvent.InvokeAsync(user);