Browse Source

Merge pull request #1 from RogueException/dev

Update fork
pull/1120/head
FakeZane GitHub 7 years ago
parent
commit
731b7108a8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 493 additions and 288 deletions
  1. +1
    -6
      README.md
  2. +6
    -1
      src/Discord.Net.Commands/Attributes/CommandAttribute.cs
  3. +2
    -2
      src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs
  4. +3
    -3
      src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
  5. +2
    -2
      src/Discord.Net.Commands/CommandService.cs
  6. +2
    -2
      src/Discord.Net.Commands/Info/CommandInfo.cs
  7. +2
    -2
      src/Discord.Net.Commands/Map/CommandMap.cs
  8. +5
    -6
      src/Discord.Net.Commands/Map/CommandMapNode.cs
  9. +4
    -4
      src/Discord.Net.Commands/PrimitiveParsers.cs
  10. +2
    -3
      src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs
  11. +2
    -2
      src/Discord.Net.Commands/Readers/UserTypeReader.cs
  12. +4
    -1
      src/Discord.Net.Commands/Results/ParseResult.cs
  13. +4
    -1
      src/Discord.Net.Commands/Results/PreconditionGroupResult.cs
  14. +4
    -1
      src/Discord.Net.Commands/Results/PreconditionResult.cs
  15. +4
    -1
      src/Discord.Net.Commands/Results/SearchResult.cs
  16. +2
    -0
      src/Discord.Net.Commands/Results/TypeReaderResult.cs
  17. +2
    -2
      src/Discord.Net.Commands/Utilities/ReflectionUtils.cs
  18. +2
    -2
      src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs
  19. +2
    -2
      src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs
  20. +2
    -2
      src/Discord.Net.Core/Entities/Messages/EmbedImage.cs
  21. +2
    -2
      src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs
  22. +2
    -2
      src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs
  23. +11
    -0
      src/Discord.Net.Core/Entities/Roles/Color.cs
  24. +2
    -2
      src/Discord.Net.Core/Extensions/DiscordClientExtensions.cs
  25. +2
    -2
      src/Discord.Net.Core/Format.cs
  26. +9
    -9
      src/Discord.Net.Core/Utils/MentionUtils.cs
  27. +3
    -3
      src/Discord.Net.Core/Utils/Paging/PagedEnumerator.cs
  28. +4
    -6
      src/Discord.Net.Core/Utils/Permissions.cs
  29. +2
    -2
      src/Discord.Net.Core/Utils/Preconditions.cs
  30. +46
    -0
      src/Discord.Net.Core/Utils/TokenUtils.cs
  31. +1
    -1
      src/Discord.Net.Rest/API/Common/AuditLogOptions.cs
  32. +18
    -3
      src/Discord.Net.Rest/BaseDiscordClient.cs
  33. +1
    -1
      src/Discord.Net.Rest/ClientHelper.cs
  34. +15
    -13
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  35. +3
    -5
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs
  36. +3
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs
  37. +8
    -8
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs
  38. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs
  39. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs
  40. +2
    -2
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs
  41. +20
    -20
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs
  42. +7
    -7
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs
  43. +7
    -7
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs
  44. +10
    -10
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs
  45. +1
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs
  46. +8
    -8
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs
  47. +3
    -5
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs
  48. +5
    -7
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs
  49. +6
    -6
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs
  50. +5
    -5
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs
  51. +5
    -5
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs
  52. +10
    -10
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs
  53. +3
    -3
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs
  54. +4
    -4
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs
  55. +7
    -7
      src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs
  56. +4
    -1
      src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs
  57. +1
    -1
      src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs
  58. +1
    -1
      src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs
  59. +1
    -1
      src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs
  60. +3
    -3
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  61. +2
    -2
      src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs
  62. +2
    -2
      src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs
  63. +2
    -2
      src/Discord.Net.Rest/Net/DefaultRestClient.cs
  64. +7
    -7
      src/Discord.Net.Rest/Net/Queue/ClientBucket.cs
  65. +3
    -3
      src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
  66. +1
    -1
      src/Discord.Net.Rest/Net/RateLimitInfo.cs
  67. +9
    -9
      src/Discord.Net.WebSocket/Audio/AudioClient.cs
  68. +5
    -5
      src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs
  69. +2
    -2
      src/Discord.Net.WebSocket/ClientState.cs
  70. +4
    -4
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  71. +1
    -1
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  72. +11
    -11
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  73. +2
    -2
      src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
  74. +1
    -1
      src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs
  75. +3
    -3
      src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
  76. +1
    -1
      src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
  77. +5
    -5
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  78. +2
    -2
      src/Discord.Net.WebSocket/Entities/Messages/MessageCache.cs
  79. +1
    -1
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
  80. +1
    -1
      src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs
  81. +124
    -0
      test/Discord.Net.Tests/Tests.TokenUtils.cs

+ 1
- 6
README.md View File

@@ -16,13 +16,9 @@ Our stable builds available from NuGet through the Discord.Net metapackage:
The individual components may also be installed from NuGet: The individual components may also be installed from NuGet:
- [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/) - [Discord.Net.Commands](https://www.nuget.org/packages/Discord.Net.Commands/)
- [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/) - [Discord.Net.Rest](https://www.nuget.org/packages/Discord.Net.Rest/)
- [Discord.Net.Rpc](https://www.nuget.org/packages/Discord.Net.Rpc/)
- [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/) - [Discord.Net.WebSocket](https://www.nuget.org/packages/Discord.Net.WebSocket/)
- [Discord.Net.Webhook](https://www.nuget.org/packages/Discord.Net.Webhook/) - [Discord.Net.Webhook](https://www.nuget.org/packages/Discord.Net.Webhook/)


The following provider is available for platforms not supporting .NET Standard 1.3:
- [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/)

### Unstable (MyGet) ### Unstable (MyGet)
Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`). Nightly builds are available through our MyGet feed (`https://www.myget.org/F/discord-net/api/v3/index.json`).


@@ -41,5 +37,4 @@ The .NET Core workload must be selected during Visual Studio installation.
## Known Issues ## Known Issues


### WebSockets (Win7 and earlier) ### WebSockets (Win7 and earlier)
.NET Core 1.1 does not support WebSockets on Win7 and earlier. It's recommended to use the Discord.Net.Providers.WS4Net package until this is resolved.
Track the issue [here](https://github.com/dotnet/corefx/issues/9503).
.NET Core 1.1 does not support WebSockets on Win7 and earlier. This issue has been fixed since the release of .NET Core 2.1. It is recommended to target .NET Core 2.1 or above for your project if you wish to run your bot on legacy platforms; alternatively, you may choose to install the [Discord.Net.Providers.WS4Net](https://www.nuget.org/packages/Discord.Net.Providers.WS4Net/) package.

+ 6
- 1
src/Discord.Net.Commands/Attributes/CommandAttribute.cs View File

@@ -7,7 +7,7 @@ namespace Discord.Commands
{ {
public string Text { get; } public string Text { get; }
public RunMode RunMode { get; set; } = RunMode.Default; public RunMode RunMode { get; set; } = RunMode.Default;
public bool? IgnoreExtraArgs { get; set; }
public bool? IgnoreExtraArgs { get; }


public CommandAttribute() public CommandAttribute()
{ {
@@ -17,5 +17,10 @@ namespace Discord.Commands
{ {
Text = text; Text = text;
} }
public CommandAttribute(string text, bool ignoreExtraArgs)
{
Text = text;
IgnoreExtraArgs = ignoreExtraArgs;
}
} }
} }

+ 2
- 2
src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs View File

@@ -7,13 +7,13 @@ namespace Discord.Commands
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class OverrideTypeReaderAttribute : Attribute public class OverrideTypeReaderAttribute : Attribute
{ {
private static readonly TypeInfo _typeReaderTypeInfo = typeof(TypeReader).GetTypeInfo();
private static readonly TypeInfo TypeReaderTypeInfo = typeof(TypeReader).GetTypeInfo();


public Type TypeReader { get; } public Type TypeReader { get; }


public OverrideTypeReaderAttribute(Type overridenTypeReader) public OverrideTypeReaderAttribute(Type overridenTypeReader)
{ {
if (!_typeReaderTypeInfo.IsAssignableFrom(overridenTypeReader.GetTypeInfo()))
if (!TypeReaderTypeInfo.IsAssignableFrom(overridenTypeReader.GetTypeInfo()))
throw new ArgumentException($"{nameof(overridenTypeReader)} must inherit from {nameof(TypeReader)}"); throw new ArgumentException($"{nameof(overridenTypeReader)} must inherit from {nameof(TypeReader)}");
TypeReader = overridenTypeReader; TypeReader = overridenTypeReader;


+ 3
- 3
src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs View File

@@ -10,7 +10,7 @@ namespace Discord.Commands
{ {
internal static class ModuleClassBuilder internal static class ModuleClassBuilder
{ {
private static readonly TypeInfo _moduleTypeInfo = typeof(IModuleBase).GetTypeInfo();
private static readonly TypeInfo ModuleTypeInfo = typeof(IModuleBase).GetTypeInfo();


public static async Task<IReadOnlyList<TypeInfo>> SearchAsync(Assembly assembly, CommandService service) public static async Task<IReadOnlyList<TypeInfo>> SearchAsync(Assembly assembly, CommandService service)
{ {
@@ -135,7 +135,7 @@ namespace Discord.Commands
if (builder.Name == null) if (builder.Name == null)
builder.Name = typeInfo.Name; builder.Name = typeInfo.Name;


var validCommands = typeInfo.DeclaredMethods.Where(x => IsValidCommandDefinition(x));
var validCommands = typeInfo.DeclaredMethods.Where(IsValidCommandDefinition);


foreach (var method in validCommands) foreach (var method in validCommands)
{ {
@@ -299,7 +299,7 @@ namespace Discord.Commands


private static bool IsValidModuleDefinition(TypeInfo typeInfo) private static bool IsValidModuleDefinition(TypeInfo typeInfo)
{ {
return _moduleTypeInfo.IsAssignableFrom(typeInfo) &&
return ModuleTypeInfo.IsAssignableFrom(typeInfo) &&
!typeInfo.IsAbstract && !typeInfo.IsAbstract &&
!typeInfo.ContainsGenericParameters; !typeInfo.ContainsGenericParameters;
} }


+ 2
- 2
src/Discord.Net.Commands/CommandService.cs View File

@@ -118,7 +118,7 @@ namespace Discord.Commands
var typeInfo = type.GetTypeInfo(); var typeInfo = type.GetTypeInfo();


if (_typedModuleDefs.ContainsKey(type)) if (_typedModuleDefs.ContainsKey(type))
throw new ArgumentException($"This module has already been added.");
throw new ArgumentException("This module has already been added.");


var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault(); var module = (await ModuleClassBuilder.BuildAsync(this, services, typeInfo).ConfigureAwait(false)).FirstOrDefault();


@@ -241,7 +241,7 @@ namespace Discord.Commands
{ {
if (_defaultTypeReaders.ContainsKey(type)) if (_defaultTypeReaders.ContainsKey(type))
_ = _cmdLogger.WarningAsync($"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." + _ = _cmdLogger.WarningAsync($"The default TypeReader for {type.FullName} was replaced by {reader.GetType().FullName}." +
$"To suppress this message, use AddTypeReader<T>(reader, true).");
"To suppress this message, use AddTypeReader<T>(reader, true).");
AddTypeReader(type, reader, true); AddTypeReader(type, reader, true);
} }
/// <summary> /// <summary>


+ 2
- 2
src/Discord.Net.Commands/Info/CommandInfo.cs View File

@@ -1,4 +1,4 @@
using Discord.Commands.Builders;
using Discord.Commands.Builders;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -63,7 +63,7 @@ namespace Discord.Commands
Attributes = builder.Attributes.ToImmutableArray(); Attributes = builder.Attributes.ToImmutableArray();


Parameters = builder.Parameters.Select(x => x.Build(this)).ToImmutableArray(); Parameters = builder.Parameters.Select(x => x.Build(this)).ToImmutableArray();
HasVarArgs = builder.Parameters.Count > 0 ? builder.Parameters[builder.Parameters.Count - 1].IsMultiple : false;
HasVarArgs = builder.Parameters.Count > 0 && builder.Parameters[builder.Parameters.Count - 1].IsMultiple;
IgnoreExtraArgs = builder.IgnoreExtraArgs; IgnoreExtraArgs = builder.IgnoreExtraArgs;


_action = builder.Callback; _action = builder.Callback;


+ 2
- 2
src/Discord.Net.Commands/Map/CommandMap.cs View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -6,7 +6,7 @@ namespace Discord.Commands
{ {
private readonly CommandService _service; private readonly CommandService _service;
private readonly CommandMapNode _root; private readonly CommandMapNode _root;
private static readonly string[] _blankAliases = new[] { "" };
private static readonly string[] BlankAliases = { "" };


public CommandMap(CommandService service) public CommandMap(CommandService service)
{ {


+ 5
- 6
src/Discord.Net.Commands/Map/CommandMapNode.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -7,7 +7,7 @@ namespace Discord.Commands
{ {
internal class CommandMapNode internal class CommandMapNode
{ {
private static readonly char[] _whitespaceChars = new[] { ' ', '\r', '\n' };
private static readonly char[] WhitespaceChars = { ' ', '\r', '\n' };


private readonly ConcurrentDictionary<string, CommandMapNode> _nodes; private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
private readonly string _name; private readonly string _name;
@@ -52,7 +52,6 @@ namespace Discord.Commands
public void RemoveCommand(CommandService service, string text, int index, CommandInfo command) public void RemoveCommand(CommandService service, string text, int index, CommandInfo command)
{ {
int nextSegment = NextSegment(text, index, service._separatorChar); int nextSegment = NextSegment(text, index, service._separatorChar);
string name;


lock (_lockObj) lock (_lockObj)
{ {
@@ -60,13 +59,13 @@ namespace Discord.Commands
_commands = _commands.Remove(command); _commands = _commands.Remove(command);
else else
{ {
string name;
if (nextSegment == -1) if (nextSegment == -1)
name = text.Substring(index); name = text.Substring(index);
else else
name = text.Substring(index, nextSegment - index); name = text.Substring(index, nextSegment - index);


CommandMapNode nextNode;
if (_nodes.TryGetValue(name, out nextNode))
if (_nodes.TryGetValue(name, out var nextNode))
{ {
nextNode.RemoveCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command); nextNode.RemoveCommand(service, nextSegment == -1 ? "" : text, nextSegment + 1, command);
if (nextNode.IsEmpty) if (nextNode.IsEmpty)
@@ -100,7 +99,7 @@ namespace Discord.Commands
} }


//Check if this is the last command segment before args //Check if this is the last command segment before args
nextSegment = NextSegment(text, index, _whitespaceChars, service._separatorChar);
nextSegment = NextSegment(text, index, WhitespaceChars, service._separatorChar);
if (nextSegment != -1) if (nextSegment != -1)
{ {
name = text.Substring(index, nextSegment - index); name = text.Substring(index, nextSegment - index);


+ 4
- 4
src/Discord.Net.Commands/PrimitiveParsers.cs View File

@@ -8,9 +8,9 @@ namespace Discord.Commands


internal static class PrimitiveParsers internal static class PrimitiveParsers
{ {
private static readonly Lazy<IReadOnlyDictionary<Type, Delegate>> _parsers = new Lazy<IReadOnlyDictionary<Type, Delegate>>(CreateParsers);
private static readonly Lazy<IReadOnlyDictionary<Type, Delegate>> Parsers = new Lazy<IReadOnlyDictionary<Type, Delegate>>(CreateParsers);


public static IEnumerable<Type> SupportedTypes = _parsers.Value.Keys;
public static IEnumerable<Type> SupportedTypes = Parsers.Value.Keys;


static IReadOnlyDictionary<Type, Delegate> CreateParsers() static IReadOnlyDictionary<Type, Delegate> CreateParsers()
{ {
@@ -34,7 +34,7 @@ namespace Discord.Commands
return parserBuilder.ToImmutable(); return parserBuilder.ToImmutable();
} }


public static TryParseDelegate<T> Get<T>() => (TryParseDelegate<T>)_parsers.Value[typeof(T)];
public static Delegate Get(Type type) => _parsers.Value[type];
public static TryParseDelegate<T> Get<T>() => (TryParseDelegate<T>)Parsers.Value[typeof(T)];
public static Delegate Get(Type type) => Parsers.Value[type];
} }
} }

+ 2
- 3
src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs View File

@@ -6,8 +6,7 @@ namespace Discord.Commands
{ {
internal class TimeSpanTypeReader : TypeReader internal class TimeSpanTypeReader : TypeReader
{ {
private static readonly string[] _formats = new[]
{
private static readonly string[] Formats = {
"%d'd'%h'h'%m'm'%s's'", //4d3h2m1s "%d'd'%h'h'%m'm'%s's'", //4d3h2m1s
"%d'd'%h'h'%m'm'", //4d3h2m "%d'd'%h'h'%m'm'", //4d3h2m
"%d'd'%h'h'%s's'", //4d3h 1s "%d'd'%h'h'%s's'", //4d3h 1s
@@ -27,7 +26,7 @@ namespace Discord.Commands


public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services) public override Task<TypeReaderResult> ReadAsync(ICommandContext context, string input, IServiceProvider services)
{ {
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), _formats, CultureInfo.InvariantCulture, out var timeSpan))
return (TimeSpan.TryParseExact(input.ToLowerInvariant(), Formats, CultureInfo.InvariantCulture, out var timeSpan))
? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan)) ? Task.FromResult(TypeReaderResult.FromSuccess(timeSpan))
: Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan")); : Task.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Failed to parse TimeSpan"));
} }


+ 2
- 2
src/Discord.Net.Commands/Readers/UserTypeReader.cs View File

@@ -71,8 +71,8 @@ namespace Discord.Commands
.Where(x => string.Equals(input, (x as IGuildUser)?.Nickname, StringComparison.OrdinalIgnoreCase)) .Where(x => string.Equals(input, (x as IGuildUser)?.Nickname, StringComparison.OrdinalIgnoreCase))
.ForEachAsync(channelUser => AddResult(results, channelUser as T, (channelUser as IGuildUser).Nickname == input ? 0.65f : 0.55f)); .ForEachAsync(channelUser => AddResult(results, channelUser as T, (channelUser as IGuildUser).Nickname == input ? 0.65f : 0.55f));


foreach (var guildUser in guildUsers.Where(x => string.Equals(input, (x as IGuildUser).Nickname, StringComparison.OrdinalIgnoreCase)))
AddResult(results, guildUser as T, (guildUser as IGuildUser).Nickname == input ? 0.60f : 0.50f);
foreach (var guildUser in guildUsers.Where(x => string.Equals(input, x.Nickname, StringComparison.OrdinalIgnoreCase)))
AddResult(results, guildUser as T, guildUser.Nickname == input ? 0.60f : 0.50f);
} }


if (results.Count > 0) if (results.Count > 0)


+ 4
- 1
src/Discord.Net.Commands/Results/ParseResult.cs View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;


namespace Discord.Commands namespace Discord.Commands
@@ -53,6 +54,8 @@ namespace Discord.Commands


public static ParseResult FromError(CommandError error, string reason) public static ParseResult FromError(CommandError error, string reason)
=> new ParseResult(null, null, error, reason); => new ParseResult(null, null, error, reason);
public static ParseResult FromError(Exception ex)
=> FromError(CommandError.Exception, ex.Message);
public static ParseResult FromError(IResult result) public static ParseResult FromError(IResult result)
=> new ParseResult(null, null, result.Error, result.ErrorReason); => new ParseResult(null, null, result.Error, result.ErrorReason);




+ 4
- 1
src/Discord.Net.Commands/Results/PreconditionGroupResult.cs View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;


namespace Discord.Commands namespace Discord.Commands
@@ -18,6 +19,8 @@ namespace Discord.Commands
=> new PreconditionGroupResult(null, null, null); => new PreconditionGroupResult(null, null, null);
public static PreconditionGroupResult FromError(string reason, ICollection<PreconditionResult> preconditions) public static PreconditionGroupResult FromError(string reason, ICollection<PreconditionResult> preconditions)
=> new PreconditionGroupResult(CommandError.UnmetPrecondition, reason, preconditions); => new PreconditionGroupResult(CommandError.UnmetPrecondition, reason, preconditions);
public static new PreconditionGroupResult FromError(Exception ex)
=> new PreconditionGroupResult(CommandError.Exception, ex.Message, null);
public static new PreconditionGroupResult FromError(IResult result) //needed? public static new PreconditionGroupResult FromError(IResult result) //needed?
=> new PreconditionGroupResult(result.Error, result.ErrorReason, null); => new PreconditionGroupResult(result.Error, result.ErrorReason, null);




+ 4
- 1
src/Discord.Net.Commands/Results/PreconditionResult.cs View File

@@ -1,4 +1,5 @@
using System.Diagnostics;
using System;
using System.Diagnostics;


namespace Discord.Commands namespace Discord.Commands
{ {
@@ -20,6 +21,8 @@ namespace Discord.Commands
=> new PreconditionResult(null, null); => new PreconditionResult(null, null);
public static PreconditionResult FromError(string reason) public static PreconditionResult FromError(string reason)
=> new PreconditionResult(CommandError.UnmetPrecondition, reason); => new PreconditionResult(CommandError.UnmetPrecondition, reason);
public static PreconditionResult FromError(Exception ex)
=> new PreconditionResult(CommandError.Exception, ex.Message);
public static PreconditionResult FromError(IResult result) public static PreconditionResult FromError(IResult result)
=> new PreconditionResult(result.Error, result.ErrorReason); => new PreconditionResult(result.Error, result.ErrorReason);




+ 4
- 1
src/Discord.Net.Commands/Results/SearchResult.cs View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;


namespace Discord.Commands namespace Discord.Commands
@@ -26,6 +27,8 @@ namespace Discord.Commands
=> new SearchResult(text, commands, null, null); => new SearchResult(text, commands, null, null);
public static SearchResult FromError(CommandError error, string reason) public static SearchResult FromError(CommandError error, string reason)
=> new SearchResult(null, null, error, reason); => new SearchResult(null, null, error, reason);
public static SearchResult FromError(Exception ex)
=> FromError(CommandError.Exception, ex.Message);
public static SearchResult FromError(IResult result) public static SearchResult FromError(IResult result)
=> new SearchResult(null, null, result.Error, result.ErrorReason); => new SearchResult(null, null, result.Error, result.ErrorReason);




+ 2
- 0
src/Discord.Net.Commands/Results/TypeReaderResult.cs View File

@@ -50,6 +50,8 @@ namespace Discord.Commands
=> new TypeReaderResult(values, null, null); => new TypeReaderResult(values, null, null);
public static TypeReaderResult FromError(CommandError error, string reason) public static TypeReaderResult FromError(CommandError error, string reason)
=> new TypeReaderResult(null, error, reason); => new TypeReaderResult(null, error, reason);
public static TypeReaderResult FromError(Exception ex)
=> FromError(CommandError.Exception, ex.Message);
public static TypeReaderResult FromError(IResult result) public static TypeReaderResult FromError(IResult result)
=> new TypeReaderResult(null, result.Error, result.ErrorReason); => new TypeReaderResult(null, result.Error, result.ErrorReason);




+ 2
- 2
src/Discord.Net.Commands/Utilities/ReflectionUtils.cs View File

@@ -8,7 +8,7 @@ namespace Discord.Commands
{ {
internal static class ReflectionUtils internal static class ReflectionUtils
{ {
private static readonly TypeInfo _objectTypeInfo = typeof(object).GetTypeInfo();
private static readonly TypeInfo ObjectTypeInfo = typeof(object).GetTypeInfo();


internal static T CreateObject<T>(TypeInfo typeInfo, CommandService commands, IServiceProvider services = null) internal static T CreateObject<T>(TypeInfo typeInfo, CommandService commands, IServiceProvider services = null)
=> CreateBuilder<T>(typeInfo, commands)(services); => CreateBuilder<T>(typeInfo, commands)(services);
@@ -54,7 +54,7 @@ namespace Discord.Commands
private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo ownerType) private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo ownerType)
{ {
var result = new List<System.Reflection.PropertyInfo>(); var result = new List<System.Reflection.PropertyInfo>();
while (ownerType != _objectTypeInfo)
while (ownerType != ObjectTypeInfo)
{ {
foreach (var prop in ownerType.DeclaredProperties) foreach (var prop in ownerType.DeclaredProperties)
{ {


+ 2
- 2
src/Discord.Net.Core/Entities/AuditLogs/IAuditLogEntry.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -9,7 +9,7 @@ namespace Discord
/// <summary> /// <summary>
/// Represents an entry in an audit log /// Represents an entry in an audit log
/// </summary> /// </summary>
public interface IAuditLogEntry : IEntity<ulong>
public interface IAuditLogEntry : ISnowflakeEntity
{ {
/// <summary> /// <summary>
/// The action which occured to create this entry /// The action which occured to create this entry


+ 2
- 2
src/Discord.Net.Core/Entities/Messages/EmbedBuilder.cs View File

@@ -239,7 +239,7 @@ namespace Discord
get => _name; get => _name;
set set
{ {
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException($"Field name must not be null, empty or entirely whitespace.", nameof(Name));
if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("Field name must not be null, empty or entirely whitespace.", nameof(Name));
if (value.Length > MaxFieldNameLength) throw new ArgumentException($"Field name length must be less than or equal to {MaxFieldNameLength}.", nameof(Name)); if (value.Length > MaxFieldNameLength) throw new ArgumentException($"Field name length must be less than or equal to {MaxFieldNameLength}.", nameof(Name));
_name = value; _name = value;
} }
@@ -251,7 +251,7 @@ namespace Discord
set set
{ {
var stringValue = value?.ToString(); var stringValue = value?.ToString();
if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException($"Field value must not be null or empty.", nameof(Value));
if (string.IsNullOrEmpty(stringValue)) throw new ArgumentException("Field value must not be null or empty.", nameof(Value));
if (stringValue.Length > MaxFieldValueLength) throw new ArgumentException($"Field value length must be less than or equal to {MaxFieldValueLength}.", nameof(Value)); if (stringValue.Length > MaxFieldValueLength) throw new ArgumentException($"Field value length must be less than or equal to {MaxFieldValueLength}.", nameof(Value));
_value = stringValue; _value = stringValue;
} }


+ 2
- 2
src/Discord.Net.Core/Entities/Messages/EmbedImage.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics; using System.Diagnostics;


namespace Discord namespace Discord
@@ -20,6 +20,6 @@ namespace Discord
} }


private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})";
public override string ToString() => Url.ToString();
public override string ToString() => Url;
} }
} }

+ 2
- 2
src/Discord.Net.Core/Entities/Messages/EmbedThumbnail.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics; using System.Diagnostics;


namespace Discord namespace Discord
@@ -20,6 +20,6 @@ namespace Discord
} }


private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})";
public override string ToString() => Url.ToString();
public override string ToString() => Url;
} }
} }

+ 2
- 2
src/Discord.Net.Core/Entities/Messages/EmbedVideo.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics; using System.Diagnostics;


namespace Discord namespace Discord
@@ -18,6 +18,6 @@ namespace Discord
} }


private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})"; private string DebuggerDisplay => $"{Url} ({(Width != null && Height != null ? $"{Width}x{Height}" : "0x0")})";
public override string ToString() => Url.ToString();
public override string ToString() => Url;
} }
} }

+ 11
- 0
src/Discord.Net.Core/Entities/Roles/Color.cs View File

@@ -100,6 +100,17 @@ namespace Discord
(uint)(b * 255.0f); (uint)(b * 255.0f);
} }


public static bool operator ==(Color lhs, Color rhs)
=> lhs.RawValue == rhs.RawValue;

public static bool operator !=(Color lhs, Color rhs)
=> lhs.RawValue != rhs.RawValue;

public override bool Equals(object obj)
=> (obj is Color c && RawValue == c.RawValue);

public override int GetHashCode() => RawValue.GetHashCode();

#if NETSTANDARD2_0 || NET45 #if NETSTANDARD2_0 || NET45
public static implicit operator StandardColor(Color color) => public static implicit operator StandardColor(Color color) =>
StandardColor.FromArgb((int)color.RawValue); StandardColor.FromArgb((int)color.RawValue);


+ 2
- 2
src/Discord.Net.Core/Extensions/DiscordClientExtensions.cs View File

@@ -12,12 +12,12 @@ namespace Discord
public static async Task<IDMChannel> GetDMChannelAsync(this IDiscordClient client, ulong id) public static async Task<IDMChannel> GetDMChannelAsync(this IDiscordClient client, ulong id)
=> await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IDMChannel; => await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IDMChannel;
public static async Task<IEnumerable<IDMChannel>> GetDMChannelsAsync(this IDiscordClient client) public static async Task<IEnumerable<IDMChannel>> GetDMChannelsAsync(this IDiscordClient client)
=> (await client.GetPrivateChannelsAsync().ConfigureAwait(false)).Select(x => x as IDMChannel).Where(x => x != null);
=> (await client.GetPrivateChannelsAsync().ConfigureAwait(false)).OfType<IDMChannel>();


public static async Task<IGroupChannel> GetGroupChannelAsync(this IDiscordClient client, ulong id) public static async Task<IGroupChannel> GetGroupChannelAsync(this IDiscordClient client, ulong id)
=> await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IGroupChannel; => await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IGroupChannel;
public static async Task<IEnumerable<IGroupChannel>> GetGroupChannelsAsync(this IDiscordClient client) public static async Task<IEnumerable<IGroupChannel>> GetGroupChannelsAsync(this IDiscordClient client)
=> (await client.GetPrivateChannelsAsync().ConfigureAwait(false)).Select(x => x as IGroupChannel).Where(x => x != null);
=> (await client.GetPrivateChannelsAsync().ConfigureAwait(false)).OfType<IGroupChannel>();


public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this IDiscordClient discord) public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this IDiscordClient discord)
{ {


+ 2
- 2
src/Discord.Net.Core/Format.cs View File

@@ -1,9 +1,9 @@
namespace Discord
namespace Discord
{ {
public static class Format public static class Format
{ {
// Characters which need escaping // Characters which need escaping
private static string[] SensitiveCharacters = { "\\", "*", "_", "~", "`" };
private static readonly string[] SensitiveCharacters = { "\\", "*", "_", "~", "`" };


/// <summary> Returns a markdown-formatted string with bold formatting. </summary> /// <summary> Returns a markdown-formatted string with bold formatting. </summary>
public static string Bold(string text) => $"**{text}**"; public static string Bold(string text) => $"**{text}**";


+ 9
- 9
src/Discord.Net.Core/Utils/MentionUtils.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Globalization; using System.Globalization;
using System.Text; using System.Text;


@@ -139,22 +139,22 @@ namespace Discord
if (user != null) if (user != null)
return $"@{guildUser?.Nickname ?? user?.Username}"; return $"@{guildUser?.Nickname ?? user?.Username}";
else else
return $"";
return "";
case TagHandling.NameNoPrefix: case TagHandling.NameNoPrefix:
if (user != null) if (user != null)
return $"{guildUser?.Nickname ?? user?.Username}"; return $"{guildUser?.Nickname ?? user?.Username}";
else else
return $"";
return "";
case TagHandling.FullName: case TagHandling.FullName:
if (user != null) if (user != null)
return $"@{user.Username}#{user.Discriminator}"; return $"@{user.Username}#{user.Discriminator}";
else else
return $"";
return "";
case TagHandling.FullNameNoPrefix: case TagHandling.FullNameNoPrefix:
if (user != null) if (user != null)
return $"{user.Username}#{user.Discriminator}"; return $"{user.Username}#{user.Discriminator}";
else else
return $"";
return "";
case TagHandling.Sanitize: case TagHandling.Sanitize:
if (guildUser != null && guildUser.Nickname == null) if (guildUser != null && guildUser.Nickname == null)
return MentionUser($"{SanitizeChar}{tag.Key}", false); return MentionUser($"{SanitizeChar}{tag.Key}", false);
@@ -176,13 +176,13 @@ namespace Discord
if (channel != null) if (channel != null)
return $"#{channel.Name}"; return $"#{channel.Name}";
else else
return $"";
return "";
case TagHandling.NameNoPrefix: case TagHandling.NameNoPrefix:
case TagHandling.FullNameNoPrefix: case TagHandling.FullNameNoPrefix:
if (channel != null) if (channel != null)
return $"{channel.Name}"; return $"{channel.Name}";
else else
return $"";
return "";
case TagHandling.Sanitize: case TagHandling.Sanitize:
return MentionChannel($"{SanitizeChar}{tag.Key}"); return MentionChannel($"{SanitizeChar}{tag.Key}");
} }
@@ -201,13 +201,13 @@ namespace Discord
if (role != null) if (role != null)
return $"@{role.Name}"; return $"@{role.Name}";
else else
return $"";
return "";
case TagHandling.NameNoPrefix: case TagHandling.NameNoPrefix:
case TagHandling.FullNameNoPrefix: case TagHandling.FullNameNoPrefix:
if (role != null) if (role != null)
return $"{role.Name}"; return $"{role.Name}";
else else
return $"";
return "";
case TagHandling.Sanitize: case TagHandling.Sanitize:
return MentionRole($"{SanitizeChar}{tag.Key}"); return MentionRole($"{SanitizeChar}{tag.Key}");
} }


+ 3
- 3
src/Discord.Net.Core/Utils/Paging/PagedEnumerator.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -60,7 +60,7 @@ namespace Discord
if (Current.Count == 0) if (Current.Count == 0)
_info.Remaining = 0; _info.Remaining = 0;
} }
_info.PageSize = _info.Remaining != null ? (int)Math.Min(_info.Remaining.Value, _source.PageSize) : _source.PageSize;
_info.PageSize = _info.Remaining != null ? Math.Min(_info.Remaining.Value, _source.PageSize) : _source.PageSize;


if (_info.Remaining != 0) if (_info.Remaining != 0)
{ {
@@ -74,4 +74,4 @@ namespace Discord
public void Dispose() { Current = null; } public void Dispose() { Current = null; }
} }
} }
}
}

+ 4
- 6
src/Discord.Net.Core/Utils/Permissions.cs View File

@@ -1,4 +1,4 @@
using System.Runtime.CompilerServices;
using System.Runtime.CompilerServices;


namespace Discord namespace Discord
{ {
@@ -119,13 +119,11 @@ namespace Discord
resolvedPermissions = mask; //Owners and administrators always have all permissions resolvedPermissions = mask; //Owners and administrators always have all permissions
else else
{ {
OverwritePermissions? perms;

//Start with this user's guild permissions //Start with this user's guild permissions
resolvedPermissions = guildPermissions; resolvedPermissions = guildPermissions;


//Give/Take Everyone permissions //Give/Take Everyone permissions
perms = channel.GetPermissionOverwrite(guild.EveryoneRole);
var perms = channel.GetPermissionOverwrite(guild.EveryoneRole);
if (perms != null) if (perms != null)
resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue;


@@ -133,7 +131,7 @@ namespace Discord
ulong deniedPermissions = 0UL, allowedPermissions = 0UL; ulong deniedPermissions = 0UL, allowedPermissions = 0UL;
foreach (var roleId in user.RoleIds) foreach (var roleId in user.RoleIds)
{ {
IRole role = null;
IRole role;
if (roleId != guild.EveryoneRole.Id && (role = guild.GetRole(roleId)) != null) if (roleId != guild.EveryoneRole.Id && (role = guild.GetRole(roleId)) != null)
{ {
perms = channel.GetPermissionOverwrite(role); perms = channel.GetPermissionOverwrite(role);
@@ -151,7 +149,7 @@ namespace Discord
if (perms != null) if (perms != null)
resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue; resolvedPermissions = (resolvedPermissions & ~perms.Value.DenyValue) | perms.Value.AllowValue;


if (channel is ITextChannel textChannel)
if (channel is ITextChannel)
{ {
if (!GetValue(resolvedPermissions, ChannelPermission.ViewChannel)) if (!GetValue(resolvedPermissions, ChannelPermission.ViewChannel))
{ {


+ 2
- 2
src/Discord.Net.Core/Utils/Preconditions.cs View File

@@ -1,4 +1,4 @@
using System;
using System;


namespace Discord namespace Discord
{ {
@@ -198,7 +198,7 @@ namespace Discord
for (var i = 0; i < roles.Length; i++) for (var i = 0; i < roles.Length; i++)
{ {
if (roles[i] == guildId) if (roles[i] == guildId)
throw new ArgumentException($"The everyone role cannot be assigned to a user", name);
throw new ArgumentException("The everyone role cannot be assigned to a user", name);
} }
} }
} }


+ 46
- 0
src/Discord.Net.Core/Utils/TokenUtils.cs View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Discord
{
public static class TokenUtils
{
/// <summary>
/// Checks the validity of the supplied token of a specific type.
/// </summary>
/// <param name="tokenType"> The type of token to validate. </param>
/// <param name="token"> The token value to validate. </param>
/// <exception cref="ArgumentNullException"> Thrown when the supplied token string is null, empty, or contains only whitespace.</exception>
/// <exception cref="ArgumentException"> Thrown when the supplied TokenType or token value is invalid. </exception>
public static void ValidateToken(TokenType tokenType, string token)
{
// A Null or WhiteSpace token of any type is invalid.
if (string.IsNullOrWhiteSpace(token))
throw new ArgumentNullException("A token cannot be null, empty, or contain only whitespace.", nameof(token));

switch (tokenType)
{
case TokenType.Webhook:
// no validation is performed on Webhook tokens
break;
case TokenType.Bearer:
// no validation is performed on Bearer tokens
break;
case TokenType.Bot:
// bot tokens are assumed to be at least 59 characters in length
// this value was determined by referencing examples in the discord documentation, and by comparing with
// pre-existing tokens
if (token.Length < 59)
throw new ArgumentException("A Bot token must be at least 59 characters in length.", nameof(token));
break;
default:
// All unrecognized TokenTypes (including User tokens) are considered to be invalid.
throw new ArgumentException("Unrecognized TokenType.", nameof(token));
}
}

}
}

+ 1
- 1
src/Discord.Net.Rest/API/Common/AuditLogOptions.cs View File

@@ -20,7 +20,7 @@ namespace Discord.API
[JsonProperty("role_name")] [JsonProperty("role_name")]
public string OverwriteRoleName { get; set; } public string OverwriteRoleName { get; set; }
[JsonProperty("type")] [JsonProperty("type")]
public string OverwriteType { get; set; }
public PermissionTarget OverwriteType { get; set; }
[JsonProperty("id")] [JsonProperty("id")]
public ulong? OverwriteTargetId { get; set; } public ulong? OverwriteTargetId { get; set; }
} }


+ 18
- 3
src/Discord.Net.Rest/BaseDiscordClient.cs View File

@@ -55,11 +55,11 @@ namespace Discord.Rest
await _stateLock.WaitAsync().ConfigureAwait(false); await _stateLock.WaitAsync().ConfigureAwait(false);
try try
{ {
await LoginInternalAsync(tokenType, token).ConfigureAwait(false);
await LoginInternalAsync(tokenType, token, validateToken).ConfigureAwait(false);
} }
finally { _stateLock.Release(); } finally { _stateLock.Release(); }
} }
private async Task LoginInternalAsync(TokenType tokenType, string token)
private async Task LoginInternalAsync(TokenType tokenType, string token, bool validateToken)
{ {
if (_isFirstLogin) if (_isFirstLogin)
{ {
@@ -73,11 +73,26 @@ namespace Discord.Rest


try try
{ {
// If token validation is enabled, validate the token and let it throw any ArgumentExceptions
// that result from invalid parameters
if (validateToken)
{
try
{
TokenUtils.ValidateToken(tokenType, token);
}
catch (ArgumentException ex)
{
// log these ArgumentExceptions and allow for the client to attempt to log in anyways
await LogManager.WarningAsync("Discord", "A supplied token was invalid", ex).ConfigureAwait(false);
}
}

await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false); await ApiClient.LoginAsync(tokenType, token).ConfigureAwait(false);
await OnLoginAsync(tokenType, token).ConfigureAwait(false); await OnLoginAsync(tokenType, token).ConfigureAwait(false);
LoginState = LoginState.LoggedIn; LoginState = LoginState.LoggedIn;
} }
catch (Exception)
catch
{ {
await LogoutInternalAsync().ConfigureAwait(false); await LogoutInternalAsync().ConfigureAwait(false);
throw; throw;


+ 1
- 1
src/Discord.Net.Rest/ClientHelper.cs View File

@@ -47,7 +47,7 @@ namespace Discord.Rest
public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client, RequestOptions options) public static async Task<IReadOnlyCollection<RestConnection>> GetConnectionsAsync(BaseDiscordClient client, RequestOptions options)
{ {
var models = await client.ApiClient.GetMyConnectionsAsync(options).ConfigureAwait(false); var models = await client.ApiClient.GetMyConnectionsAsync(options).ConfigureAwait(false);
return models.Select(x => RestConnection.Create(x)).ToImmutableArray();
return models.Select(RestConnection.Create).ToImmutableArray();
} }
public static async Task<RestInviteMetadata> GetInviteAsync(BaseDiscordClient client, public static async Task<RestInviteMetadata> GetInviteAsync(BaseDiscordClient client,


+ 15
- 13
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -43,9 +43,11 @@ namespace Discord.API
public TokenType AuthTokenType { get; private set; } public TokenType AuthTokenType { get; private set; }
internal string AuthToken { get; private set; } internal string AuthToken { get; private set; }
internal IRestClient RestClient { get; private set; } internal IRestClient RestClient { get; private set; }
internal ulong? CurrentUserId { get; set;}
internal ulong? CurrentUserId { get; set; }


public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry,
internal JsonSerializer Serializer => _serializer;

public DiscordRestApiClient(RestClientProvider restClientProvider, string userAgent, RetryMode defaultRetryMode = RetryMode.AlwaysRetry,
JsonSerializer serializer = null) JsonSerializer serializer = null)
{ {
_restClientProvider = restClientProvider; _restClientProvider = restClientProvider;
@@ -123,7 +125,7 @@ namespace Discord.API


LoginState = LoginState.LoggedIn; LoginState = LoginState.LoggedIn;
} }
catch (Exception)
catch
{ {
await LogoutInternalAsync().ConfigureAwait(false); await LogoutInternalAsync().ConfigureAwait(false);
throw; throw;
@@ -235,7 +237,7 @@ namespace Discord.API
internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids, internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,
ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null) ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null, [CallerMemberName] string funcName = null)
=> SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options); => SendMultipartAsync<TResponse>(method, GetEndpoint(endpointExpr), multipartArgs, GetBucketId(ids, endpointExpr, AuthTokenType, funcName), clientBucket, options);
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
public async Task<TResponse> SendMultipartAsync<TResponse>(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs,
string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null) string bucketId = null, ClientBucketType clientBucket = ClientBucketType.Unbucketed, RequestOptions options = null)
{ {
options = options ?? new RequestOptions(); options = options ?? new RequestOptions();
@@ -414,7 +416,7 @@ namespace Discord.API
var ids = new BucketIds(guildId: guildId); var ids = new BucketIds(guildId: guildId);
await SendAsync("DELETE", () => $"guilds/{guildId}/members/{userId}/roles/{roleId}", ids, options: options); await SendAsync("DELETE", () => $"guilds/{guildId}/members/{userId}/roles/{roleId}", ids, options: options);
} }
//Channel Messages //Channel Messages
public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null) public async Task<Message> GetChannelMessageAsync(ulong channelId, ulong messageId, RequestOptions options = null)
{ {
@@ -490,7 +492,7 @@ namespace Discord.API
if (args.Content?.Length > DiscordConfig.MaxMessageSize) if (args.Content?.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);
return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, new BucketIds(), clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); return await SendJsonAsync<Message>("POST", () => $"webhooks/{webhookId}/{AuthToken}?wait=true", args, new BucketIds(), clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false);
} }
public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null) public async Task<Message> UploadFileAsync(ulong channelId, UploadFileParams args, RequestOptions options = null)
@@ -737,7 +739,7 @@ namespace Discord.API
Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name)); Preconditions.NotNullOrWhitespace(args.Name, nameof(args.Name));
Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId)); Preconditions.NotNullOrWhitespace(args.RegionId, nameof(args.RegionId));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);
return await SendJsonAsync<Guild>("POST", () => "guilds", args, new BucketIds(), options: options).ConfigureAwait(false); return await SendJsonAsync<Guild>("POST", () => "guilds", args, new BucketIds(), options: options).ConfigureAwait(false);
} }
public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null) public async Task<Guild> DeleteGuildAsync(ulong guildId, RequestOptions options = null)
@@ -964,7 +966,7 @@ namespace Discord.API
{ {
Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId)); Preconditions.NotNullOrEmpty(inviteId, nameof(inviteId));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);
return await SendAsync<Invite>("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false); return await SendAsync<Invite>("DELETE", () => $"invites/{inviteId}", new BucketIds(), options: options).ConfigureAwait(false);
} }


@@ -1163,7 +1165,7 @@ namespace Discord.API


int limit = args.Limit.GetValueOrDefault(int.MaxValue); int limit = args.Limit.GetValueOrDefault(int.MaxValue);
ulong afterGuildId = args.AfterGuildId.GetValueOrDefault(0); ulong afterGuildId = args.AfterGuildId.GetValueOrDefault(0);
return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false); return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", () => $"users/@me/guilds?limit={limit}&after={afterGuildId}", new BucketIds(), options: options).ConfigureAwait(false);
} }
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null) public async Task<Application> GetMyApplicationAsync(RequestOptions options = null)
@@ -1263,7 +1265,7 @@ namespace Discord.API
Preconditions.NotNull(args, nameof(args)); Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name)); Preconditions.NotNullOrEmpty(args.Name, nameof(args.Name));
options = RequestOptions.CreateOrClone(options); options = RequestOptions.CreateOrClone(options);
if (AuthTokenType == TokenType.Webhook) if (AuthTokenType == TokenType.Webhook)
return await SendJsonAsync<Webhook>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}", args, new BucketIds(), options: options).ConfigureAwait(false); return await SendJsonAsync<Webhook>("PATCH", () => $"webhooks/{webhookId}/{AuthToken}", args, new BucketIds(), options: options).ConfigureAwait(false);
else else
@@ -1392,9 +1394,9 @@ namespace Discord.API


int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1)); int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1));
string fieldName = GetFieldName(methodArgs[argId + 1]); string fieldName = GetFieldName(methodArgs[argId + 1]);
int? mappedId;
mappedId = BucketIds.GetIndex(fieldName);
var mappedId = BucketIds.GetIndex(fieldName);
if(!mappedId.HasValue && rightIndex != endIndex && format.Length > rightIndex + 1 && format[rightIndex + 1] == '/') //Ignore the next slash if(!mappedId.HasValue && rightIndex != endIndex && format.Length > rightIndex + 1 && format[rightIndex + 1] == '/') //Ignore the next slash
rightIndex++; rightIndex++;




+ 3
- 5
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs View File

@@ -26,18 +26,16 @@ namespace Discord.Rest
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");


var type = typeModel.NewValue.ToObject<ChannelType>();
var name = nameModel.NewValue.ToObject<string>();
var type = typeModel.NewValue.ToObject<ChannelType>(discord.ApiClient.Serializer);
var name = nameModel.NewValue.ToObject<string>(discord.ApiClient.Serializer);


foreach (var overwrite in overwritesModel.NewValue) foreach (var overwrite in overwritesModel.NewValue)
{ {
var deny = overwrite.Value<ulong>("deny"); var deny = overwrite.Value<ulong>("deny");
var _type = overwrite.Value<string>("type");
var permType = overwrite.Value<PermissionTarget>("type");
var id = overwrite.Value<ulong>("id"); var id = overwrite.Value<ulong>("id");
var allow = overwrite.Value<ulong>("allow"); var allow = overwrite.Value<ulong>("allow");


PermissionTarget permType = _type == "member" ? PermissionTarget.User : PermissionTarget.Role;

overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny))); overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny)));
} }




+ 3
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs View File

@@ -27,11 +27,11 @@ namespace Discord.Rest
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");


var overwrites = overwritesModel.OldValue.ToObject<API.Overwrite[]>()
var overwrites = overwritesModel.OldValue.ToObject<API.Overwrite[]>(discord.ApiClient.Serializer)
.Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny))) .Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny)))
.ToList(); .ToList();
var type = typeModel.OldValue.ToObject<ChannelType>();
var name = nameModel.OldValue.ToObject<string>();
var type = typeModel.OldValue.ToObject<ChannelType>(discord.ApiClient.Serializer);
var name = nameModel.OldValue.ToObject<string>(discord.ApiClient.Serializer);
var id = entry.TargetId.Value; var id = entry.TargetId.Value;


return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection());


+ 8
- 8
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs View File

@@ -23,14 +23,14 @@ namespace Discord.Rest
var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate");
var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit"); var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit");


string oldName = nameModel?.OldValue?.ToObject<string>(),
newName = nameModel?.NewValue?.ToObject<string>();
string oldTopic = topicModel?.OldValue?.ToObject<string>(),
newTopic = topicModel?.NewValue?.ToObject<string>();
int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(),
newBitrate = bitrateModel?.NewValue?.ToObject<int>();
int? oldLimit = userLimitModel?.OldValue?.ToObject<int>(),
newLimit = userLimitModel?.NewValue?.ToObject<int>();
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
string oldTopic = topicModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newTopic = topicModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
int? oldBitrate = bitrateModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newBitrate = bitrateModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);
int? oldLimit = userLimitModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newLimit = userLimitModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);


var before = new ChannelInfo(oldName, oldTopic, oldBitrate, oldLimit); var before = new ChannelInfo(oldName, oldTopic, oldBitrate, oldLimit);
var after = new ChannelInfo(newName, newTopic, newBitrate, newLimit); var after = new ChannelInfo(newName, newTopic, newBitrate, newLimit);


+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteCreateAuditLogData.cs View File

@@ -21,7 +21,7 @@ namespace Discord.Rest
{ {
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");


var emoteName = change.NewValue?.ToObject<string>();
var emoteName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName); return new EmoteCreateAuditLogData(entry.TargetId.Value, emoteName);
} }




+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteDeleteAuditLogData.cs View File

@@ -17,7 +17,7 @@ namespace Discord.Rest
{ {
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");


var emoteName = change.OldValue?.ToObject<string>();
var emoteName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer);


return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName); return new EmoteDeleteAuditLogData(entry.TargetId.Value, emoteName);
} }


+ 2
- 2
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/EmoteUpdateAuditLogData.cs View File

@@ -18,8 +18,8 @@ namespace Discord.Rest
{ {
var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name"); var change = entry.Changes.FirstOrDefault(x => x.ChangedProperty == "name");


var newName = change.NewValue?.ToObject<string>();
var oldName = change.OldValue?.ToObject<string>();
var newName = change.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
var oldName = change.OldValue?.ToObject<string>(discord.ApiClient.Serializer);


return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName); return new EmoteUpdateAuditLogData(entry.TargetId.Value, oldName, newName);
} }


+ 20
- 20
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs View File

@@ -28,26 +28,26 @@ namespace Discord.Rest
var mfaLevelModel = 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"); var contentFilterModel = changes.FirstOrDefault(x => x.ChangedProperty == "afk_timeout");


int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject<int>(),
newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject<int>();
DefaultMessageNotifications? oldDefaultMessageNotifications = defaultMessageNotificationsModel?.OldValue?.ToObject<DefaultMessageNotifications>(),
newDefaultMessageNotifications = defaultMessageNotificationsModel?.NewValue?.ToObject<DefaultMessageNotifications>();
ulong? oldAfkChannelId = afkChannelModel?.OldValue?.ToObject<ulong>(),
newAfkChannelId = afkChannelModel?.NewValue?.ToObject<ulong>();
string oldName = nameModel?.OldValue?.ToObject<string>(),
newName = nameModel?.NewValue?.ToObject<string>();
string oldRegionId = regionIdModel?.OldValue?.ToObject<string>(),
newRegionId = regionIdModel?.NewValue?.ToObject<string>();
string oldIconHash = iconHashModel?.OldValue?.ToObject<string>(),
newIconHash = iconHashModel?.NewValue?.ToObject<string>();
VerificationLevel? oldVerificationLevel = verificationLevelModel?.OldValue?.ToObject<VerificationLevel>(),
newVerificationLevel = verificationLevelModel?.NewValue?.ToObject<VerificationLevel>();
ulong? oldOwnerId = ownerIdModel?.OldValue?.ToObject<ulong>(),
newOwnerId = ownerIdModel?.NewValue?.ToObject<ulong>();
MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject<MfaLevel>(),
newMfaLevel = mfaLevelModel?.NewValue?.ToObject<MfaLevel>();
int? oldContentFilter = contentFilterModel?.OldValue?.ToObject<int>(),
newContentFilter = contentFilterModel?.NewValue?.ToObject<int>();
int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);
DefaultMessageNotifications? oldDefaultMessageNotifications = defaultMessageNotificationsModel?.OldValue?.ToObject<DefaultMessageNotifications>(discord.ApiClient.Serializer),
newDefaultMessageNotifications = defaultMessageNotificationsModel?.NewValue?.ToObject<DefaultMessageNotifications>(discord.ApiClient.Serializer);
ulong? oldAfkChannelId = afkChannelModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer),
newAfkChannelId = afkChannelModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
string oldRegionId = regionIdModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newRegionId = regionIdModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
string oldIconHash = iconHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newIconHash = iconHashModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
VerificationLevel? oldVerificationLevel = verificationLevelModel?.OldValue?.ToObject<VerificationLevel>(discord.ApiClient.Serializer),
newVerificationLevel = verificationLevelModel?.NewValue?.ToObject<VerificationLevel>(discord.ApiClient.Serializer);
ulong? oldOwnerId = ownerIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer),
newOwnerId = ownerIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject<MfaLevel>(discord.ApiClient.Serializer),
newMfaLevel = mfaLevelModel?.NewValue?.ToObject<MfaLevel>(discord.ApiClient.Serializer);
int? oldContentFilter = contentFilterModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newContentFilter = contentFilterModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);


IUser oldOwner = null; IUser oldOwner = null;
if (oldOwnerId != null) if (oldOwnerId != null)


+ 7
- 7
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteCreateAuditLogData.cs View File

@@ -30,13 +30,13 @@ namespace Discord.Rest
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses");
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses");


var maxAge = maxAgeModel.NewValue.ToObject<int>();
var code = codeModel.NewValue.ToObject<string>();
var temporary = temporaryModel.NewValue.ToObject<bool>();
var inviterId = inviterIdModel.NewValue.ToObject<ulong>();
var channelId = channelIdModel.NewValue.ToObject<ulong>();
var uses = usesModel.NewValue.ToObject<int>();
var maxUses = maxUsesModel.NewValue.ToObject<int>();
var maxAge = maxAgeModel.NewValue.ToObject<int>(discord.ApiClient.Serializer);
var code = codeModel.NewValue.ToObject<string>(discord.ApiClient.Serializer);
var temporary = temporaryModel.NewValue.ToObject<bool>(discord.ApiClient.Serializer);
var inviterId = inviterIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var channelId = channelIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var uses = usesModel.NewValue.ToObject<int>(discord.ApiClient.Serializer);
var maxUses = maxUsesModel.NewValue.ToObject<int>(discord.ApiClient.Serializer);


var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId);
var inviter = RestUser.Create(discord, inviterInfo); var inviter = RestUser.Create(discord, inviterInfo);


+ 7
- 7
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteDeleteAuditLogData.cs View File

@@ -30,13 +30,13 @@ namespace Discord.Rest
var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses"); var usesModel = changes.FirstOrDefault(x => x.ChangedProperty == "uses");
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses");


var maxAge = maxAgeModel.OldValue.ToObject<int>();
var code = codeModel.OldValue.ToObject<string>();
var temporary = temporaryModel.OldValue.ToObject<bool>();
var inviterId = inviterIdModel.OldValue.ToObject<ulong>();
var channelId = channelIdModel.OldValue.ToObject<ulong>();
var uses = usesModel.OldValue.ToObject<int>();
var maxUses = maxUsesModel.OldValue.ToObject<int>();
var maxAge = maxAgeModel.OldValue.ToObject<int>(discord.ApiClient.Serializer);
var code = codeModel.OldValue.ToObject<string>(discord.ApiClient.Serializer);
var temporary = temporaryModel.OldValue.ToObject<bool>(discord.ApiClient.Serializer);
var inviterId = inviterIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var channelId = channelIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var uses = usesModel.OldValue.ToObject<int>(discord.ApiClient.Serializer);
var maxUses = maxUsesModel.OldValue.ToObject<int>(discord.ApiClient.Serializer);


var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId); var inviterInfo = log.Users.FirstOrDefault(x => x.Id == inviterId);
var inviter = RestUser.Create(discord, inviterInfo); var inviter = RestUser.Create(discord, inviterInfo);


+ 10
- 10
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/InviteUpdateAuditLogData.cs View File

@@ -23,16 +23,16 @@ namespace Discord.Rest
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses"); var maxUsesModel = changes.FirstOrDefault(x => x.ChangedProperty == "max_uses");


int? oldMaxAge = maxAgeModel?.OldValue?.ToObject<int>(),
newMaxAge = maxAgeModel?.NewValue?.ToObject<int>();
string oldCode = codeModel?.OldValue?.ToObject<string>(),
newCode = codeModel?.NewValue?.ToObject<string>();
bool? oldTemporary = temporaryModel?.OldValue?.ToObject<bool>(),
newTemporary = temporaryModel?.NewValue?.ToObject<bool>();
ulong? oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(),
newChannelId = channelIdModel?.NewValue?.ToObject<ulong>();
int? oldMaxUses = maxUsesModel?.OldValue?.ToObject<int>(),
newMaxUses = maxUsesModel?.NewValue?.ToObject<int>();
int? oldMaxAge = maxAgeModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newMaxAge = maxAgeModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);
string oldCode = codeModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newCode = codeModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
bool? oldTemporary = temporaryModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer),
newTemporary = temporaryModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
ulong? oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer),
newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
int? oldMaxUses = maxUsesModel?.OldValue?.ToObject<int>(discord.ApiClient.Serializer),
newMaxUses = maxUsesModel?.NewValue?.ToObject<int>(discord.ApiClient.Serializer);


var before = new InviteInfo(oldMaxAge, oldCode, oldTemporary, oldChannelId, oldMaxUses); var before = new InviteInfo(oldMaxAge, oldCode, oldTemporary, oldChannelId, oldMaxUses);
var after = new InviteInfo(newMaxAge, newCode, newTemporary, newChannelId, newMaxUses); var after = new InviteInfo(newMaxAge, newCode, newTemporary, newChannelId, newMaxUses);


+ 1
- 1
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberRoleAuditLogData.cs View File

@@ -19,7 +19,7 @@ namespace Discord.Rest
{ {
var changes = entry.Changes; var changes = entry.Changes;


var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(),
var roleInfos = changes.SelectMany(x => x.NewValue.ToObject<API.Role[]>(discord.ApiClient.Serializer),
(model, role) => new { model.ChangedProperty, Role = role }) (model, role) => new { model.ChangedProperty, Role = role })
.Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add")) .Select(x => new MemberRoleEditInfo(x.Role.Name, x.Role.Id, x.ChangedProperty == "$add"))
.ToList(); .ToList();


+ 8
- 8
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs View File

@@ -24,14 +24,14 @@ namespace Discord.Rest
var muteModel = changes.FirstOrDefault(x => x.ChangedProperty == "mute"); var muteModel = changes.FirstOrDefault(x => x.ChangedProperty == "mute");
var avatarModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); var avatarModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash");


string oldNick = nickModel?.OldValue?.ToObject<string>(),
newNick = nickModel?.NewValue?.ToObject<string>();
bool? oldDeaf = deafModel?.OldValue?.ToObject<bool>(),
newDeaf = deafModel?.NewValue?.ToObject<bool>();
bool? oldMute = muteModel?.OldValue?.ToObject<bool>(),
newMute = muteModel?.NewValue?.ToObject<bool>();
string oldAvatar = avatarModel?.OldValue?.ToObject<string>(),
newAvatar = avatarModel?.NewValue?.ToObject<string>();
string oldNick = nickModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newNick = nickModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
bool? oldDeaf = deafModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer),
newDeaf = deafModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
bool? oldMute = muteModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer),
newMute = muteModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
string oldAvatar = avatarModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newAvatar = avatarModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);


var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId);
var user = RestUser.Create(discord, targetInfo); var user = RestUser.Create(discord, targetInfo);


+ 3
- 5
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs View File

@@ -19,17 +19,15 @@ namespace Discord.Rest
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");


var deny = denyModel.NewValue.ToObject<ulong>();
var allow = allowModel.NewValue.ToObject<ulong>();
var deny = denyModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var allow = allowModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);


var permissions = new OverwritePermissions(allow, deny); var permissions = new OverwritePermissions(allow, deny);


var id = entry.Options.OverwriteTargetId.Value; var id = entry.Options.OverwriteTargetId.Value;
var type = entry.Options.OverwriteType; var type = entry.Options.OverwriteType;


PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role;

return new OverwriteCreateAuditLogData(new Overwrite(id, target, permissions));
return new OverwriteCreateAuditLogData(new Overwrite(id, type, permissions));
} }


public Overwrite Overwrite { get; } public Overwrite Overwrite { get; }


+ 5
- 7
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs View File

@@ -27,14 +27,12 @@ namespace Discord.Rest
var idModel = changes.FirstOrDefault(x => x.ChangedProperty == "id"); var idModel = changes.FirstOrDefault(x => x.ChangedProperty == "id");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");


var deny = denyModel.OldValue.ToObject<ulong>();
var type = typeModel.OldValue.ToObject<string>();
var id = idModel.OldValue.ToObject<ulong>();
var allow = allowModel.OldValue.ToObject<ulong>();
var deny = denyModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var type = typeModel.OldValue.ToObject<PermissionTarget>(discord.ApiClient.Serializer);
var id = idModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var allow = allowModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);


PermissionTarget target = type == "member" ? PermissionTarget.User : PermissionTarget.Role;

return new OverwriteDeleteAuditLogData(new Overwrite(id, target, new OverwritePermissions(allow, deny)));
return new OverwriteDeleteAuditLogData(new Overwrite(id, type, new OverwritePermissions(allow, deny)));
} }


public Overwrite Overwrite { get; } public Overwrite Overwrite { get; }


+ 6
- 6
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs View File

@@ -22,17 +22,17 @@ namespace Discord.Rest
var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny"); var denyModel = changes.FirstOrDefault(x => x.ChangedProperty == "deny");
var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow"); var allowModel = changes.FirstOrDefault(x => x.ChangedProperty == "allow");


var beforeAllow = allowModel?.OldValue?.ToObject<ulong>();
var afterAllow = allowModel?.NewValue?.ToObject<ulong>();
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>();
var afterDeny = denyModel?.OldValue?.ToObject<ulong>();
var beforeAllow = allowModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var afterAllow = allowModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var beforeDeny = denyModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var afterDeny = denyModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);


var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0); var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0);
var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0); var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0);


PermissionTarget target = entry.Options.OverwriteType == "member" ? PermissionTarget.User : PermissionTarget.Role;
var type = entry.Options.OverwriteType;


return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, target);
return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, type);
} }


public OverwritePermissions OldPermissions { get; } public OverwritePermissions OldPermissions { get; }


+ 5
- 5
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleCreateAuditLogData.cs View File

@@ -23,11 +23,11 @@ namespace Discord.Rest
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions");


uint? colorRaw = colorModel?.NewValue?.ToObject<uint>();
bool? mentionable = mentionableModel?.NewValue?.ToObject<bool>();
bool? hoist = hoistModel?.NewValue?.ToObject<bool>();
string name = nameModel?.NewValue?.ToObject<string>();
ulong? permissionsRaw = permissionsModel?.NewValue?.ToObject<ulong>();
uint? colorRaw = colorModel?.NewValue?.ToObject<uint>(discord.ApiClient.Serializer);
bool? mentionable = mentionableModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
bool? hoist = hoistModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
string name = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
ulong? permissionsRaw = permissionsModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);


Color? color = null; Color? color = null;
GuildPermissions? permissions = null; GuildPermissions? permissions = null;


+ 5
- 5
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleDeleteAuditLogData.cs View File

@@ -23,11 +23,11 @@ namespace Discord.Rest
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions");


uint? colorRaw = colorModel?.OldValue?.ToObject<uint>();
bool? mentionable = mentionableModel?.OldValue?.ToObject<bool>();
bool? hoist = hoistModel?.OldValue?.ToObject<bool>();
string name = nameModel?.OldValue?.ToObject<string>();
ulong? permissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>();
uint? colorRaw = colorModel?.OldValue?.ToObject<uint>(discord.ApiClient.Serializer);
bool? mentionable = mentionableModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer);
bool? hoist = hoistModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer);
string name = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer);
ulong? permissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);


Color? color = null; Color? color = null;
GuildPermissions? permissions = null; GuildPermissions? permissions = null;


+ 10
- 10
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs View File

@@ -24,16 +24,16 @@ namespace Discord.Rest
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions"); var permissionsModel = changes.FirstOrDefault(x => x.ChangedProperty == "permissions");


uint? oldColorRaw = colorModel?.OldValue?.ToObject<uint>(),
newColorRaw = colorModel?.NewValue?.ToObject<uint>();
bool? oldMentionable = mentionableModel?.OldValue?.ToObject<bool>(),
newMentionable = mentionableModel?.NewValue?.ToObject<bool>();
bool? oldHoist = hoistModel?.OldValue?.ToObject<bool>(),
newHoist = hoistModel?.NewValue?.ToObject<bool>();
string oldName = nameModel?.OldValue?.ToObject<string>(),
newName = nameModel?.NewValue?.ToObject<string>();
ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(),
newPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>();
uint? oldColorRaw = colorModel?.OldValue?.ToObject<uint>(discord.ApiClient.Serializer),
newColorRaw = colorModel?.NewValue?.ToObject<uint>(discord.ApiClient.Serializer);
bool? oldMentionable = mentionableModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer),
newMentionable = mentionableModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
bool? oldHoist = hoistModel?.OldValue?.ToObject<bool>(discord.ApiClient.Serializer),
newHoist = hoistModel?.NewValue?.ToObject<bool>(discord.ApiClient.Serializer);
string oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer),
newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer),
newPermissionsRaw = permissionsModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);


Color? oldColor = null, Color? oldColor = null,
newColor = null; newColor = null;


+ 3
- 3
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookCreateAuditLogData.cs View File

@@ -23,9 +23,9 @@ namespace Discord.Rest
var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type");
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");


var channelId = channelIdModel.NewValue.ToObject<ulong>();
var type = typeModel.NewValue.ToObject<WebhookType>();
var name = nameModel.NewValue.ToObject<string>();
var channelId = channelIdModel.NewValue.ToObject<ulong>(discord.ApiClient.Serializer);
var type = typeModel.NewValue.ToObject<WebhookType>(discord.ApiClient.Serializer);
var name = nameModel.NewValue.ToObject<string>(discord.ApiClient.Serializer);


var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId);
var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo); var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo);


+ 4
- 4
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookDeleteAuditLogData.cs View File

@@ -29,10 +29,10 @@ namespace Discord.Rest
var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name");
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash");


var channelId = channelIdModel.OldValue.ToObject<ulong>();
var type = typeModel.OldValue.ToObject<WebhookType>();
var name = nameModel.OldValue.ToObject<string>();
var avatarHash = avatarHashModel?.OldValue?.ToObject<string>();
var channelId = channelIdModel.OldValue.ToObject<ulong>(discord.ApiClient.Serializer);
var type = typeModel.OldValue.ToObject<WebhookType>(discord.ApiClient.Serializer);
var name = nameModel.OldValue.ToObject<string>(discord.ApiClient.Serializer);
var avatarHash = avatarHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer);


return new WebhookDeleteAuditLogData(entry.TargetId.Value, channelId, type, name, avatarHash); return new WebhookDeleteAuditLogData(entry.TargetId.Value, channelId, type, name, avatarHash);
} }


+ 7
- 7
src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/WebhookUpdateAuditLogData.cs View File

@@ -26,18 +26,18 @@ namespace Discord.Rest
var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id"); var channelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "channel_id");
var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); var avatarHashModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash");


var oldName = nameModel?.OldValue?.ToObject<string>();
var oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>();
var oldAvatar = avatarHashModel?.OldValue?.ToObject<string>();
var oldName = nameModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer);
var oldChannelId = channelIdModel?.OldValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var oldAvatar = avatarHashModel?.OldValue?.ToObject<string>(discord.ApiClient.Serializer);
var before = new WebhookInfo(oldName, oldChannelId, oldAvatar); var before = new WebhookInfo(oldName, oldChannelId, oldAvatar);


var newName = nameModel?.NewValue?.ToObject<string>();
var newChannelId = channelIdModel?.NewValue?.ToObject<ulong>();
var newAvatar = avatarHashModel?.NewValue?.ToObject<string>();
var newName = nameModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
var newChannelId = channelIdModel?.NewValue?.ToObject<ulong>(discord.ApiClient.Serializer);
var newAvatar = avatarHashModel?.NewValue?.ToObject<string>(discord.ApiClient.Serializer);
var after = new WebhookInfo(newName, newChannelId, newAvatar); var after = new WebhookInfo(newName, newChannelId, newAvatar);


var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId); var webhookInfo = log.Webhooks?.FirstOrDefault(x => x.Id == entry.TargetId);
var webhook = RestWebhook.Create(discord, (IGuild)null, webhookInfo);
var webhook = webhookInfo != null ? RestWebhook.Create(discord, (IGuild)null, webhookInfo) : null;


return new WebhookUpdateAuditLogData(webhook, before, after); return new WebhookUpdateAuditLogData(webhook, before, after);
} }


+ 4
- 1
src/Discord.Net.Rest/Entities/AuditLogs/RestAuditLogEntry.cs View File

@@ -1,4 +1,5 @@
using System.Linq;
using System;
using System.Linq;


using Model = Discord.API.AuditLog; using Model = Discord.API.AuditLog;
using EntryModel = Discord.API.AuditLogEntry; using EntryModel = Discord.API.AuditLogEntry;
@@ -26,6 +27,8 @@ namespace Discord.Rest
return new RestAuditLogEntry(discord, fullLog, model, user); return new RestAuditLogEntry(discord, fullLog, model, user);
} }


/// <inheritdoc/>
public DateTimeOffset CreatedAt => SnowflakeUtils.FromSnowflake(Id);
/// <inheritdoc/> /// <inheritdoc/>
public ActionType Action { get; } public ActionType Action { get; }
/// <inheritdoc/> /// <inheritdoc/>


+ 1
- 1
src/Discord.Net.Rest/Entities/Channels/RestDMChannel.cs View File

@@ -10,7 +10,7 @@ using Model = Discord.API.Channel;
namespace Discord.Rest namespace Discord.Rest
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel, IUpdateable
public class RestDMChannel : RestChannel, IDMChannel, IRestPrivateChannel, IRestMessageChannel
{ {
public RestUser CurrentUser { get; private set; } public RestUser CurrentUser { get; private set; }
public RestUser Recipient { get; private set; } public RestUser Recipient { get; private set; }


+ 1
- 1
src/Discord.Net.Rest/Entities/Channels/RestGroupChannel.cs View File

@@ -11,7 +11,7 @@ using Model = Discord.API.Channel;
namespace Discord.Rest namespace Discord.Rest
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestGroupChannel : RestChannel, IGroupChannel, IRestPrivateChannel, IRestMessageChannel, IRestAudioChannel, IUpdateable
public class RestGroupChannel : RestChannel, IGroupChannel, IRestPrivateChannel, IRestMessageChannel, IRestAudioChannel
{ {
private string _iconId; private string _iconId;
private ImmutableDictionary<ulong, RestGroupUser> _users; private ImmutableDictionary<ulong, RestGroupUser> _users;


+ 1
- 1
src/Discord.Net.Rest/Entities/Channels/RestGuildChannel.cs View File

@@ -7,7 +7,7 @@ using Model = Discord.API.Channel;


namespace Discord.Rest namespace Discord.Rest
{ {
public class RestGuildChannel : RestChannel, IGuildChannel, IUpdateable
public class RestGuildChannel : RestChannel, IGuildChannel
{ {
private ImmutableArray<Overwrite> _overwrites; private ImmutableArray<Overwrite> _overwrites;




+ 3
- 3
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -168,7 +168,7 @@ namespace Discord.Rest
public async Task<IReadOnlyCollection<RestTextChannel>> GetTextChannelsAsync(RequestOptions options = null) public async Task<IReadOnlyCollection<RestTextChannel>> GetTextChannelsAsync(RequestOptions options = null)
{ {
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false);
return channels.Select(x => x as RestTextChannel).Where(x => x != null).ToImmutableArray();
return channels.OfType<RestTextChannel>().ToImmutableArray();
} }
public async Task<RestVoiceChannel> GetVoiceChannelAsync(ulong id, RequestOptions options = null) public async Task<RestVoiceChannel> GetVoiceChannelAsync(ulong id, RequestOptions options = null)
{ {
@@ -178,12 +178,12 @@ namespace Discord.Rest
public async Task<IReadOnlyCollection<RestVoiceChannel>> GetVoiceChannelsAsync(RequestOptions options = null) public async Task<IReadOnlyCollection<RestVoiceChannel>> GetVoiceChannelsAsync(RequestOptions options = null)
{ {
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false);
return channels.Select(x => x as RestVoiceChannel).Where(x => x != null).ToImmutableArray();
return channels.OfType<RestVoiceChannel>().ToImmutableArray();
} }
public async Task<IReadOnlyCollection<RestCategoryChannel>> GetCategoryChannelsAsync(RequestOptions options = null) public async Task<IReadOnlyCollection<RestCategoryChannel>> GetCategoryChannelsAsync(RequestOptions options = null)
{ {
var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false); var channels = await GuildHelper.GetChannelsAsync(this, Discord, options).ConfigureAwait(false);
return channels.Select(x => x as RestCategoryChannel).Where(x => x != null).ToImmutableArray();
return channels.OfType<RestCategoryChannel>().ToImmutableArray();
} }


public async Task<RestVoiceChannel> GetAFKChannelAsync(RequestOptions options = null) public async Task<RestVoiceChannel> GetAFKChannelAsync(RequestOptions options = null)


+ 2
- 2
src/Discord.Net.Rest/Entities/Guilds/RestUserGuild.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using Model = Discord.API.UserGuild; using Model = Discord.API.UserGuild;
@@ -6,7 +6,7 @@ using Model = Discord.API.UserGuild;
namespace Discord.Rest namespace Discord.Rest
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestUserGuild : RestEntity<ulong>, ISnowflakeEntity, IUserGuild
public class RestUserGuild : RestEntity<ulong>, IUserGuild
{ {
private string _iconId; private string _iconId;


+ 2
- 2
src/Discord.Net.Rest/Entities/Users/RestGuildUser.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
@@ -9,7 +9,7 @@ using Model = Discord.API.GuildMember;
namespace Discord.Rest namespace Discord.Rest
{ {
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class RestGuildUser : RestUser, IGuildUser, IUpdateable
public class RestGuildUser : RestUser, IGuildUser
{ {
private long? _joinedAtTicks; private long? _joinedAtTicks;
private ImmutableArray<ulong> _roleIds; private ImmutableArray<ulong> _roleIds;


+ 2
- 2
src/Discord.Net.Rest/Net/DefaultRestClient.cs View File

@@ -131,14 +131,14 @@ namespace Discord.Net.Rest
return new RestResponse(response.StatusCode, headers, stream); return new RestResponse(response.StatusCode, headers, stream);
} }


private static readonly HttpMethod _patch = new HttpMethod("PATCH");
private static readonly HttpMethod Patch = new HttpMethod("PATCH");
private HttpMethod GetMethod(string method) private HttpMethod GetMethod(string method)
{ {
switch (method) switch (method)
{ {
case "DELETE": return HttpMethod.Delete; case "DELETE": return HttpMethod.Delete;
case "GET": return HttpMethod.Get; case "GET": return HttpMethod.Get;
case "PATCH": return _patch;
case "PATCH": return Patch;
case "POST": return HttpMethod.Post; case "POST": return HttpMethod.Post;
case "PUT": return HttpMethod.Put; case "PUT": return HttpMethod.Put;
default: throw new ArgumentOutOfRangeException(nameof(method), $"Unknown HttpMethod: {method}"); default: throw new ArgumentOutOfRangeException(nameof(method), $"Unknown HttpMethod: {method}");


+ 7
- 7
src/Discord.Net.Rest/Net/Queue/ClientBucket.cs View File

@@ -1,4 +1,4 @@
using System.Collections.Immutable;
using System.Collections.Immutable;


namespace Discord.Net.Queue namespace Discord.Net.Queue
{ {
@@ -9,8 +9,8 @@ namespace Discord.Net.Queue
} }
internal struct ClientBucket internal struct ClientBucket
{ {
private static readonly ImmutableDictionary<ClientBucketType, ClientBucket> _defsByType;
private static readonly ImmutableDictionary<string, ClientBucket> _defsById;
private static readonly ImmutableDictionary<ClientBucketType, ClientBucket> DefsByType;
private static readonly ImmutableDictionary<string, ClientBucket> DefsById;


static ClientBucket() static ClientBucket()
{ {
@@ -23,16 +23,16 @@ namespace Discord.Net.Queue
var builder = ImmutableDictionary.CreateBuilder<ClientBucketType, ClientBucket>(); var builder = ImmutableDictionary.CreateBuilder<ClientBucketType, ClientBucket>();
foreach (var bucket in buckets) foreach (var bucket in buckets)
builder.Add(bucket.Type, bucket); builder.Add(bucket.Type, bucket);
_defsByType = builder.ToImmutable();
DefsByType = builder.ToImmutable();


var builder2 = ImmutableDictionary.CreateBuilder<string, ClientBucket>(); var builder2 = ImmutableDictionary.CreateBuilder<string, ClientBucket>();
foreach (var bucket in buckets) foreach (var bucket in buckets)
builder2.Add(bucket.Id, bucket); builder2.Add(bucket.Id, bucket);
_defsById = builder2.ToImmutable();
DefsById = builder2.ToImmutable();
} }


public static ClientBucket Get(ClientBucketType type) => _defsByType[type];
public static ClientBucket Get(string id) => _defsById[id];
public static ClientBucket Get(ClientBucketType type) => DefsByType[type];
public static ClientBucket Get(string id) => DefsById[id];
public ClientBucketType Type { get; } public ClientBucketType Type { get; }
public string Id { get; } public string Id { get; }


+ 3
- 3
src/Discord.Net.Rest/Net/Queue/RequestQueue.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
#if DEBUG_LIMITS #if DEBUG_LIMITS
using System.Diagnostics; using System.Diagnostics;
@@ -16,10 +16,10 @@ namespace Discord.Net.Queue


private readonly ConcurrentDictionary<string, RequestBucket> _buckets; private readonly ConcurrentDictionary<string, RequestBucket> _buckets;
private readonly SemaphoreSlim _tokenLock; private readonly SemaphoreSlim _tokenLock;
private readonly CancellationTokenSource _cancelToken; //Dispose token
private CancellationTokenSource _clearToken; private CancellationTokenSource _clearToken;
private CancellationToken _parentToken; private CancellationToken _parentToken;
private CancellationToken _requestCancelToken; //Parent token + Clear token private CancellationToken _requestCancelToken; //Parent token + Clear token
private CancellationTokenSource _cancelToken; //Dispose token
private DateTimeOffset _waitUntil; private DateTimeOffset _waitUntil;


private Task _cleanupTask; private Task _cleanupTask;
@@ -115,7 +115,7 @@ namespace Discord.Net.Queue
foreach (var bucket in _buckets.Select(x => x.Value)) foreach (var bucket in _buckets.Select(x => x.Value))
{ {
if ((now - bucket.LastAttemptAt).TotalMinutes > 1.0) if ((now - bucket.LastAttemptAt).TotalMinutes > 1.0)
_buckets.TryRemove(bucket.Id, out RequestBucket ignored);
_buckets.TryRemove(bucket.Id, out _);
} }
await Task.Delay(60000, _cancelToken.Token); //Runs each minute await Task.Delay(60000, _cancelToken.Token); //Runs each minute
} }


+ 1
- 1
src/Discord.Net.Rest/Net/RateLimitInfo.cs View File

@@ -15,7 +15,7 @@ namespace Discord.Net
internal RateLimitInfo(Dictionary<string, string> headers) internal RateLimitInfo(Dictionary<string, string> headers)
{ {
IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) &&
bool.TryParse(temp, out var isGlobal) ? isGlobal : false;
bool.TryParse(temp, out var isGlobal) && isGlobal;
Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) &&
int.TryParse(temp, out var limit) ? limit : (int?)null; int.TryParse(temp, out var limit) ? limit : (int?)null;
Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) &&


+ 9
- 9
src/Discord.Net.WebSocket/Audio/AudioClient.cs View File

@@ -16,7 +16,7 @@ using System.Collections.Generic;
namespace Discord.Audio namespace Discord.Audio
{ {
//TODO: Add audio reconnecting //TODO: Add audio reconnecting
internal partial class AudioClient : IAudioClient, IDisposable
internal partial class AudioClient : IAudioClient
{ {
internal struct StreamPair internal struct StreamPair
{ {
@@ -65,7 +65,7 @@ namespace Discord.Audio


ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider); ApiClient = new DiscordVoiceAPIClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider);
ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false);
ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync($"Sent Discovery").ConfigureAwait(false);
ApiClient.SentDiscovery += async () => await _audioLogger.DebugAsync("Sent Discovery").ConfigureAwait(false);
//ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false); //ApiClient.SentData += async bytes => await _audioLogger.DebugAsync($"Sent {bytes} Bytes").ConfigureAwait(false);
ApiClient.ReceivedEvent += ProcessMessageAsync; ApiClient.ReceivedEvent += ProcessMessageAsync;
ApiClient.ReceivedPacket += ProcessPacketAsync; ApiClient.ReceivedPacket += ProcessPacketAsync;
@@ -131,7 +131,7 @@ namespace Discord.Audio
await keepaliveTask.ConfigureAwait(false); await keepaliveTask.ConfigureAwait(false);
_keepaliveTask = null; _keepaliveTask = null;


while (_heartbeatTimes.TryDequeue(out long time)) { }
while (_heartbeatTimes.TryDequeue(out _)) { }
_lastMessageTime = 0; _lastMessageTime = 0;


await ClearInputStreamsAsync().ConfigureAwait(false); await ClearInputStreamsAsync().ConfigureAwait(false);
@@ -292,7 +292,7 @@ namespace Discord.Audio
{ {
if (packet.Length != 70) if (packet.Length != 70)
{ {
await _audioLogger.DebugAsync($"Malformed Packet").ConfigureAwait(false);
await _audioLogger.DebugAsync("Malformed Packet").ConfigureAwait(false);
return; return;
} }
string ip; string ip;
@@ -304,7 +304,7 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.DebugAsync($"Malformed Packet", ex).ConfigureAwait(false);
await _audioLogger.DebugAsync("Malformed Packet", ex).ConfigureAwait(false);
return; return;
} }
@@ -331,7 +331,7 @@ namespace Discord.Audio
{ {
if (pair.Key == value) if (pair.Key == value)
{ {
int latency = (int)(Environment.TickCount - pair.Value);
int latency = Environment.TickCount - pair.Value;
int before = UdpLatency; int before = UdpLatency;
UdpLatency = latency; UdpLatency = latency;


@@ -344,7 +344,7 @@ namespace Discord.Audio
{ {
if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc)) if (!RTPReadStream.TryReadSsrc(packet, 0, out var ssrc))
{ {
await _audioLogger.DebugAsync($"Malformed Frame").ConfigureAwait(false);
await _audioLogger.DebugAsync("Malformed Frame").ConfigureAwait(false);
return; return;
} }
if (!_ssrcMap.TryGetValue(ssrc, out var userId)) if (!_ssrcMap.TryGetValue(ssrc, out var userId))
@@ -363,7 +363,7 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.DebugAsync($"Malformed Frame", ex).ConfigureAwait(false);
await _audioLogger.DebugAsync("Malformed Frame", ex).ConfigureAwait(false);
return; return;
} }
//await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false); //await _audioLogger.DebugAsync($"Received {packet.Length} bytes from user {userId}").ConfigureAwait(false);
@@ -372,7 +372,7 @@ namespace Discord.Audio
} }
catch (Exception ex) catch (Exception ex)
{ {
await _audioLogger.WarningAsync($"Failed to process UDP packet", ex).ConfigureAwait(false);
await _audioLogger.WarningAsync("Failed to process UDP packet", ex).ConfigureAwait(false);
return; return;
} }
} }


+ 5
- 5
src/Discord.Net.WebSocket/Audio/Streams/BufferedWriteStream.cs View File

@@ -116,7 +116,7 @@ namespace Discord.Audio.Streams
timestamp += OpusEncoder.FrameSamplesPerChannel; timestamp += OpusEncoder.FrameSamplesPerChannel;
} }
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync($"Buffer underrun");
var _ = _logger?.DebugAsync("Buffer underrun");
#endif #endif
} }
} }
@@ -140,7 +140,7 @@ namespace Discord.Audio.Streams
if (!_bufferPool.TryDequeue(out byte[] buffer)) if (!_bufferPool.TryDequeue(out byte[] buffer))
{ {
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync($"Buffer overflow"); //Should never happen because of the queueLock
var _ = _logger?.DebugAsync("Buffer overflow"); //Should never happen because of the queueLock
#endif #endif
return; return;
} }
@@ -149,7 +149,7 @@ namespace Discord.Audio.Streams
if (!_isPreloaded && _queuedFrames.Count == _queueLength) if (!_isPreloaded && _queuedFrames.Count == _queueLength)
{ {
#if DEBUG #if DEBUG
var _ = _logger?.DebugAsync($"Preloaded");
var _ = _logger?.DebugAsync("Preloaded");
#endif #endif
_isPreloaded = true; _isPreloaded = true;
} }
@@ -169,8 +169,8 @@ namespace Discord.Audio.Streams
{ {
do do
cancelToken.ThrowIfCancellationRequested(); cancelToken.ThrowIfCancellationRequested();
while (_queuedFrames.TryDequeue(out Frame ignored));
while (_queuedFrames.TryDequeue(out _));
return Task.Delay(0); return Task.Delay(0);
} }
} }
}
}

+ 2
- 2
src/Discord.Net.WebSocket/ClientState.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -72,7 +72,7 @@ namespace Discord.WebSocket
switch (channel) switch (channel)
{ {
case SocketDMChannel dmChannel: case SocketDMChannel dmChannel:
_dmChannels.TryRemove(dmChannel.Recipient.Id, out var ignored);
_dmChannels.TryRemove(dmChannel.Recipient.Id, out _);
break; break;
case SocketGroupChannel groupChannel: case SocketGroupChannel groupChannel:
_groupChannels.TryRemove(id); _groupChannels.TryRemove(id);


+ 4
- 4
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -13,11 +13,11 @@ namespace Discord.WebSocket
{ {
private readonly DiscordSocketConfig _baseConfig; private readonly DiscordSocketConfig _baseConfig;
private readonly SemaphoreSlim _connectionGroupLock; private readonly SemaphoreSlim _connectionGroupLock;
private readonly Dictionary<int, int> _shardIdsToIndex;
private readonly bool _automaticShards;
private int[] _shardIds; private int[] _shardIds;
private Dictionary<int, int> _shardIdsToIndex;
private DiscordSocketClient[] _shards; private DiscordSocketClient[] _shards;
private int _totalShards; private int _totalShards;
private bool _automaticShards;
/// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary> /// <summary> Gets the estimated round-trip latency, in milliseconds, to the gateway server. </summary>
public override int Latency { get => GetLatency(); protected set { } } public override int Latency { get => GetLatency(); protected set { } }
@@ -25,8 +25,8 @@ namespace Discord.WebSocket
public override IActivity Activity { get => _shards[0].Activity; protected set { } } public override IActivity Activity { get => _shards[0].Activity; protected set { } }


internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient; internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(() => GetGuildCount());
public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(() => GetPrivateChannelCount());
public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount);
public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => GetPrivateChannels().ToReadOnlyCollection(GetPrivateChannelCount);
public IReadOnlyCollection<DiscordSocketClient> Shards => _shards; public IReadOnlyCollection<DiscordSocketClient> Shards => _shards;
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _shards[0].VoiceRegions; public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _shards[0].VoiceRegions;




+ 1
- 1
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -26,9 +26,9 @@ namespace Discord.API
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } } public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>(); private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();


private readonly bool _isExplicitUrl;
private CancellationTokenSource _connectCancelToken; private CancellationTokenSource _connectCancelToken;
private string _gatewayUrl; private string _gatewayUrl;
private bool _isExplicitUrl;


//Store our decompression streams for zlib shared state //Store our decompression streams for zlib shared state
private MemoryStream _compressed; private MemoryStream _compressed;


+ 11
- 11
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -63,9 +63,9 @@ namespace Discord.WebSocket
public override IReadOnlyCollection<SocketGuild> Guilds => State.Guilds; public override IReadOnlyCollection<SocketGuild> Guilds => State.Guilds;
public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels; public override IReadOnlyCollection<ISocketPrivateChannel> PrivateChannels => State.PrivateChannels;
public IReadOnlyCollection<SocketDMChannel> DMChannels public IReadOnlyCollection<SocketDMChannel> DMChannels
=> State.PrivateChannels.Select(x => x as SocketDMChannel).Where(x => x != null).ToImmutableArray();
=> State.PrivateChannels.OfType<SocketDMChannel>().ToImmutableArray();
public IReadOnlyCollection<SocketGroupChannel> GroupChannels public IReadOnlyCollection<SocketGroupChannel> GroupChannels
=> State.PrivateChannels.Select(x => x as SocketGroupChannel).Where(x => x != null).ToImmutableArray();
=> State.PrivateChannels.OfType<SocketGroupChannel>().ToImmutableArray();
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();


/// <summary> Creates a new REST/WebSocket discord client. </summary> /// <summary> Creates a new REST/WebSocket discord client. </summary>
@@ -207,7 +207,7 @@ namespace Discord.WebSocket
await heartbeatTask.ConfigureAwait(false); await heartbeatTask.ConfigureAwait(false);
_heartbeatTask = null; _heartbeatTask = null;


while (_heartbeatTimes.TryDequeue(out long time)) { }
while (_heartbeatTimes.TryDequeue(out _)) { }
_lastMessageTime = 0; _lastMessageTime = 0;


await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Waiting for guild downloader").ConfigureAwait(false);
@@ -218,7 +218,7 @@ namespace Discord.WebSocket


//Clear large guild queue //Clear large guild queue
await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Clearing large guild queue").ConfigureAwait(false);
while (_largeGuilds.TryDequeue(out ulong guildId)) { }
while (_largeGuilds.TryDequeue(out _)) { }


//Raise virtual GUILD_UNAVAILABLEs //Raise virtual GUILD_UNAVAILABLEs
await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false); await _gatewayLogger.DebugAsync("Raising virtual GuildUnavailables").ConfigureAwait(false);
@@ -351,7 +351,7 @@ namespace Discord.WebSocket


var gameModel = new GameModel(); var gameModel = new GameModel();
// Discord only accepts rich presence over RPC, don't even bother building a payload // Discord only accepts rich presence over RPC, don't even bother building a payload
if (Activity is RichGame game)
if (Activity is RichGame)
throw new NotSupportedException("Outgoing Rich Presences are not supported"); throw new NotSupportedException("Outgoing Rich Presences are not supported");


if (Activity != null) if (Activity != null)
@@ -508,7 +508,7 @@ namespace Discord.WebSocket
{ {
type = "GUILD_AVAILABLE"; type = "GUILD_AVAILABLE";
_lastGuildAvailableTime = Environment.TickCount; _lastGuildAvailableTime = Environment.TickCount;
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false);
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_AVAILABLE)").ConfigureAwait(false);


var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
if (guild != null) if (guild != null)
@@ -533,7 +533,7 @@ namespace Discord.WebSocket
} }
else else
{ {
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_CREATE)").ConfigureAwait(false);


var guild = AddGuild(data, State); var guild = AddGuild(data, State);
if (guild != null) if (guild != null)
@@ -614,7 +614,7 @@ namespace Discord.WebSocket
if (data.Unavailable == true) if (data.Unavailable == true)
{ {
type = "GUILD_UNAVAILABLE"; type = "GUILD_UNAVAILABLE";
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false);
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_UNAVAILABLE)").ConfigureAwait(false);


var guild = State.GetGuild(data.Id); var guild = State.GetGuild(data.Id);
if (guild != null) if (guild != null)
@@ -630,7 +630,7 @@ namespace Discord.WebSocket
} }
else else
{ {
await _gatewayLogger.DebugAsync($"Received Dispatch (GUILD_DELETE)").ConfigureAwait(false);
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_DELETE)").ConfigureAwait(false);


var guild = RemoveGuild(data.Id); var guild = RemoveGuild(data.Id);
if (guild != null) if (guild != null)
@@ -1630,7 +1630,7 @@ namespace Discord.WebSocket
var guild = State.RemoveGuild(id); var guild = State.RemoveGuild(id);
if (guild != null) if (guild != null)
{ {
foreach (var channel in guild.Channels)
foreach (var _ in guild.Channels)
State.RemoveChannel(id); State.RemoveChannel(id);
foreach (var user in guild.Users) foreach (var user in guild.Users)
user.GlobalUser.RemoveRef(this); user.GlobalUser.RemoveRef(this);
@@ -1683,7 +1683,7 @@ namespace Discord.WebSocket
if (eventHandler.HasSubscribers) if (eventHandler.HasSubscribers)
{ {
if (HandlerTimeout.HasValue) if (HandlerTimeout.HasValue)
await TimeoutWrap(name, () => eventHandler.InvokeAsync()).ConfigureAwait(false);
await TimeoutWrap(name, eventHandler.InvokeAsync).ConfigureAwait(false);
else else
await eventHandler.InvokeAsync().ConfigureAwait(false); await eventHandler.InvokeAsync().ConfigureAwait(false);
} }


+ 2
- 2
src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs View File

@@ -39,8 +39,8 @@ namespace Discord.Audio
private readonly JsonSerializer _serializer; private readonly JsonSerializer _serializer;
private readonly SemaphoreSlim _connectionLock; private readonly SemaphoreSlim _connectionLock;
private readonly IUdpSocket _udp;
private CancellationTokenSource _connectCancelToken; private CancellationTokenSource _connectCancelToken;
private IUdpSocket _udp;
private bool _isDisposed; private bool _isDisposed;
private ulong _nextKeepalive; private ulong _nextKeepalive;


@@ -188,7 +188,7 @@ namespace Discord.Audio


ConnectionState = ConnectionState.Connected; ConnectionState = ConnectionState.Connected;
} }
catch (Exception)
catch
{ {
await DisconnectInternalAsync().ConfigureAwait(false); await DisconnectInternalAsync().ConfigureAwait(false);
throw; throw;


+ 1
- 1
src/Discord.Net.WebSocket/Entities/Channels/SocketDMChannel.cs View File

@@ -97,7 +97,7 @@ namespace Discord.WebSocket
if (id == Recipient.Id) if (id == Recipient.Id)
return Recipient; return Recipient;
else if (id == Discord.CurrentUser.Id) else if (id == Discord.CurrentUser.Id)
return Discord.CurrentUser as SocketSelfUser;
return Discord.CurrentUser;
else else
return null; return null;
} }


+ 3
- 3
src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs View File

@@ -18,10 +18,10 @@ namespace Discord.WebSocket
public class SocketGroupChannel : SocketChannel, IGroupChannel, ISocketPrivateChannel, ISocketMessageChannel, ISocketAudioChannel public class SocketGroupChannel : SocketChannel, IGroupChannel, ISocketPrivateChannel, ISocketMessageChannel, ISocketAudioChannel
{ {
private readonly MessageCache _messages; private readonly MessageCache _messages;
private readonly ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates;


private string _iconId; private string _iconId;
private ConcurrentDictionary<ulong, SocketGroupUser> _users; private ConcurrentDictionary<ulong, SocketGroupUser> _users;
private ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates;


public string Name { get; private set; } public string Name { get; private set; }


@@ -129,7 +129,7 @@ namespace Discord.WebSocket
internal SocketGroupUser GetOrAddUser(UserModel model) internal SocketGroupUser GetOrAddUser(UserModel model)
{ {
if (_users.TryGetValue(model.Id, out SocketGroupUser user)) if (_users.TryGetValue(model.Id, out SocketGroupUser user))
return user as SocketGroupUser;
return user;
else else
{ {
var privateUser = SocketGroupUser.Create(this, Discord.State, model); var privateUser = SocketGroupUser.Create(this, Discord.State, model);
@@ -143,7 +143,7 @@ namespace Discord.WebSocket
if (_users.TryRemove(id, out SocketGroupUser user)) if (_users.TryRemove(id, out SocketGroupUser user))
{ {
user.GlobalUser.RemoveRef(Discord); user.GlobalUser.RemoveRef(Discord);
return user as SocketGroupUser;
return user;
} }
return null; return null;
} }


+ 1
- 1
src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs View File

@@ -43,7 +43,7 @@ namespace Discord.WebSocket
public Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null) public Task ModifyAsync(Action<VoiceChannelProperties> func, RequestOptions options = null)
=> ChannelHelper.ModifyAsync(this, Discord, func, options); => ChannelHelper.ModifyAsync(this, Discord, func, options);


public async Task<IAudioClient> ConnectAsync(bool selfDeaf, bool selfMute, bool external)
public async Task<IAudioClient> ConnectAsync(bool selfDeaf = false, bool selfMute = false, bool external = false)
{ {
return await Guild.ConnectAudioAsync(Id, selfDeaf, selfMute, external).ConfigureAwait(false); return await Guild.ConnectAudioAsync(Id, selfDeaf, selfMute, external).ConfigureAwait(false);
} }


+ 5
- 5
src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs View File

@@ -91,11 +91,11 @@ namespace Discord.WebSocket
} }
} }
public IReadOnlyCollection<SocketTextChannel> TextChannels public IReadOnlyCollection<SocketTextChannel> TextChannels
=> Channels.Select(x => x as SocketTextChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketTextChannel>().ToImmutableArray();
public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels
=> Channels.Select(x => x as SocketVoiceChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketVoiceChannel>().ToImmutableArray();
public IReadOnlyCollection<SocketCategoryChannel> CategoryChannels public IReadOnlyCollection<SocketCategoryChannel> CategoryChannels
=> Channels.Select(x => x as SocketCategoryChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketCategoryChannel>().ToImmutableArray();
public SocketGuildUser CurrentUser => _members.TryGetValue(Discord.CurrentUser.Id, out SocketGuildUser member) ? member : null; public SocketGuildUser CurrentUser => _members.TryGetValue(Discord.CurrentUser.Id, out SocketGuildUser member) ? member : null;
public SocketRole EveryoneRole => GetRole(Id); public SocketRole EveryoneRole => GetRole(Id);
public IReadOnlyCollection<SocketGuildChannel> Channels public IReadOnlyCollection<SocketGuildChannel> Channels
@@ -563,7 +563,7 @@ namespace Discord.WebSocket


await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false); await Discord.ApiClient.SendVoiceStateUpdateAsync(Id, channelId, selfDeaf, selfMute).ConfigureAwait(false);
} }
catch (Exception)
catch
{ {
await DisconnectAudioInternalAsync().ConfigureAwait(false); await DisconnectAudioInternalAsync().ConfigureAwait(false);
throw; throw;
@@ -580,7 +580,7 @@ namespace Discord.WebSocket
throw new TimeoutException(); throw new TimeoutException();
return await promise.Task.ConfigureAwait(false); return await promise.Task.ConfigureAwait(false);
} }
catch (Exception)
catch
{ {
await DisconnectAudioAsync().ConfigureAwait(false); await DisconnectAudioAsync().ConfigureAwait(false);
throw; throw;


+ 2
- 2
src/Discord.Net.WebSocket/Entities/Messages/MessageCache.cs View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -28,7 +28,7 @@ namespace Discord.WebSocket
_orderedMessages.Enqueue(message.Id); _orderedMessages.Enqueue(message.Id);


while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out ulong msgId)) while (_orderedMessages.Count > _size && _orderedMessages.TryDequeue(out ulong msgId))
_messages.TryRemove(msgId, out SocketMessage msg);
_messages.TryRemove(msgId, out _);
} }
} }




+ 1
- 1
src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs View File

@@ -12,12 +12,12 @@ namespace Discord.WebSocket
[DebuggerDisplay(@"{DebuggerDisplay,nq}")] [DebuggerDisplay(@"{DebuggerDisplay,nq}")]
public class SocketUserMessage : SocketMessage, IUserMessage public class SocketUserMessage : SocketMessage, IUserMessage
{ {
private readonly List<SocketReaction> _reactions = new List<SocketReaction>();
private bool _isMentioningEveryone, _isTTS, _isPinned; private bool _isMentioningEveryone, _isTTS, _isPinned;
private long? _editedTimestampTicks; private long? _editedTimestampTicks;
private ImmutableArray<Attachment> _attachments; private ImmutableArray<Attachment> _attachments;
private ImmutableArray<Embed> _embeds; private ImmutableArray<Embed> _embeds;
private ImmutableArray<ITag> _tags; private ImmutableArray<ITag> _tags;
private List<SocketReaction> _reactions = new List<SocketReaction>();
public override bool IsTTS => _isTTS; public override bool IsTTS => _isTTS;
public override bool IsPinned => _isPinned; public override bool IsPinned => _isPinned;


+ 1
- 1
src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs View File

@@ -22,8 +22,8 @@ namespace Discord.Net.WebSockets


private readonly SemaphoreSlim _lock; private readonly SemaphoreSlim _lock;
private readonly Dictionary<string, string> _headers; private readonly Dictionary<string, string> _headers;
private readonly IWebProxy _proxy;
private ClientWebSocket _client; private ClientWebSocket _client;
private IWebProxy _proxy;
private Task _task; private Task _task;
private CancellationTokenSource _cancelTokenSource; private CancellationTokenSource _cancelTokenSource;
private CancellationToken _cancelToken, _parentToken; private CancellationToken _cancelToken, _parentToken;


+ 124
- 0
test/Discord.Net.Tests/Tests.TokenUtils.cs View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace Discord
{
public class TokenUtilsTests
{
/// <summary>
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
/// to see that when a null, empty or whitespace-only string is passed as the token,
/// it will throw an ArgumentNullException.
/// </summary>
[Theory]
[InlineData(null)]
[InlineData("")] // string.Empty isn't a constant type
[InlineData(" ")]
[InlineData(" ")]
[InlineData("\t")]
public void TestNullOrWhitespaceToken(string token)
{
// an ArgumentNullException should be thrown, regardless of the TokenType
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bearer, token));
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
Assert.Throws<ArgumentNullException>(() => TokenUtils.ValidateToken(TokenType.Webhook, token));
}
/// <summary>
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
/// to see that valid Webhook tokens do not throw Exceptions.
/// </summary>
/// <param name="token"></param>
[Theory]
[InlineData("123123123")]
// bot token
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
// bearer token taken from discord docs
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
// client secret
[InlineData("937it3ow87i4ery69876wqire")]
public void TestWebhookTokenDoesNotThrowExceptions(string token)
{
TokenUtils.ValidateToken(TokenType.Webhook, token);
}

// No tests for invalid webhook token behavior, because there is nothing there yet.

/// <summary>
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
/// to see that valid Webhook tokens do not throw Exceptions.
/// </summary>
/// <param name="token"></param>
[Theory]
[InlineData("123123123")]
// bot token
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
// bearer token taken from discord docs
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
// client secret
[InlineData("937it3ow87i4ery69876wqire")]
public void TestBearerTokenDoesNotThrowExceptions(string token)
{
TokenUtils.ValidateToken(TokenType.Bearer, token);
}

// No tests for invalid bearer token behavior, because there is nothing there yet.

/// <summary>
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
/// to see that valid Bot tokens do not throw Exceptions.
/// Valid Bot tokens can be strings of length 59 or above.
/// </summary>
[Theory]
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
[InlineData("This appears to be completely invalid, however the current validation rules are not very strict.")]
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWss")]
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWsMTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs")]
public void TestBotTokenDoesNotThrowExceptions(string token)
{
// This example token is pulled from the Discord Docs
// https://discordapp.com/developers/docs/reference#authentication-example-bot-token-authorization-header
// should not throw any exception
TokenUtils.ValidateToken(TokenType.Bot, token);
}

/// <summary>
/// Tests the usage of <see cref="TokenUtils.ValidateToken(TokenType, string)"/> with
/// a Bot token that is invalid.
/// </summary>
[Theory]
[InlineData("This is invalid")]
// missing a single character from the end
[InlineData("MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKW")]
// bearer token
[InlineData("6qrZcUqja7812RVdnEKjpzOL4CvHBFG")]
// client secret
[InlineData("937it3ow87i4ery69876wqire")]
public void TestBotTokenInvalidThrowsArgumentException(string token)
{
Assert.Throws<ArgumentException>(() => TokenUtils.ValidateToken(TokenType.Bot, token));
}

/// <summary>
/// Tests the behavior of <see cref="TokenUtils.ValidateToken(TokenType, string)"/>
/// to see that an <see cref="ArgumentException"/> is thrown when an invalid
/// <see cref="TokenType"/> is supplied as a parameter.
/// </summary>
/// <remarks>
/// The <see cref="TokenType.User"/> type is treated as an invalid <see cref="TokenType"/>.
/// </remarks>
[Theory]
// TokenType.User
[InlineData(0)]
// out of range TokenType
[InlineData(4)]
[InlineData(7)]
public void TestUnrecognizedTokenType(int type)
{
Assert.Throws<ArgumentException>(() =>
TokenUtils.ValidateToken((TokenType)type, "MTk4NjIyNDgzNDcxOTI1MjQ4.Cl2FMQ.ZnCjm1XVW7vRze4b7Cq4se7kKWs"));
}
}
}

Loading…
Cancel
Save