Browse Source

Merge branch 'dev' into docs/pre-release

pull/1161/head
Still Hsu 7 years ago
parent
commit
112fe72374
No known key found for this signature in database GPG Key ID: 8601A145FDA95209
32 changed files with 264 additions and 71 deletions
  1. +1
    -1
      src/Discord.Net.Commands/Attributes/OverrideTypeReaderAttribute.cs
  2. +3
    -3
      src/Discord.Net.Commands/Builders/ModuleClassBuilder.cs
  3. +2
    -2
      src/Discord.Net.Commands/Info/CommandInfo.cs
  4. +1
    -0
      src/Discord.Net.Commands/Map/CommandMap.cs
  5. +3
    -4
      src/Discord.Net.Commands/Map/CommandMapNode.cs
  6. +4
    -4
      src/Discord.Net.Commands/PrimitiveParsers.cs
  7. +2
    -3
      src/Discord.Net.Commands/Readers/TimeSpanTypeReader.cs
  8. +3
    -3
      src/Discord.Net.Commands/Utilities/ReflectionUtils.cs
  9. +11
    -0
      src/Discord.Net.Core/Entities/Roles/Color.cs
  10. +2
    -2
      src/Discord.Net.Core/Extensions/DiscordClientExtensions.cs
  11. +3
    -3
      src/Discord.Net.Core/Utils/Paging/PagedEnumerator.cs
  12. +2
    -4
      src/Discord.Net.Core/Utils/Permissions.cs
  13. +46
    -0
      src/Discord.Net.Core/Utils/TokenUtils.cs
  14. +19
    -4
      src/Discord.Net.Rest/BaseDiscordClient.cs
  15. +2
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  16. +3
    -3
      src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs
  17. +2
    -2
      src/Discord.Net.Rest/Net/DefaultRestClient.cs
  18. +7
    -7
      src/Discord.Net.Rest/Net/Queue/ClientBucket.cs
  19. +1
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueue.cs
  20. +1
    -1
      src/Discord.Net.WebSocket/Audio/AudioClient.cs
  21. +1
    -1
      src/Discord.Net.WebSocket/ClientState.cs
  22. +3
    -3
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  23. +1
    -1
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  24. +3
    -3
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  25. +1
    -1
      src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
  26. +3
    -3
      src/Discord.Net.WebSocket/Entities/Channels/SocketGroupChannel.cs
  27. +1
    -1
      src/Discord.Net.WebSocket/Entities/Channels/SocketVoiceChannel.cs
  28. +5
    -5
      src/Discord.Net.WebSocket/Entities/Guilds/SocketGuild.cs
  29. +1
    -1
      src/Discord.Net.WebSocket/Entities/Messages/MessageCache.cs
  30. +2
    -2
      src/Discord.Net.WebSocket/Entities/Messages/SocketUserMessage.cs
  31. +1
    -1
      src/Discord.Net.WebSocket/Net/DefaultWebSocketClient.cs
  32. +124
    -0
      test/Discord.Net.Tests/Tests.TokenUtils.cs

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

@@ -23,7 +23,7 @@ namespace Discord.Commands
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/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;
@@ -114,7 +114,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;


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

@@ -6,6 +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 = { "" };


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


+ 3
- 4
src/Discord.Net.Commands/Map/CommandMapNode.cs View File

@@ -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;
@@ -53,7 +53,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)
{ {
@@ -61,13 +60,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)


+ 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"));
} }


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

@@ -7,7 +7,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);
@@ -52,8 +52,8 @@ namespace Discord.Commands
} }
private static PropertyInfo[] GetProperties(TypeInfo ownerType) private static PropertyInfo[] GetProperties(TypeInfo ownerType)
{ {
var result = new List<PropertyInfo>();
while (ownerType != _objectTypeInfo)
var result = new List<System.Reflection.PropertyInfo>();
while (ownerType != ObjectTypeInfo)
{ {
foreach (var prop in ownerType.DeclaredProperties) foreach (var prop in ownerType.DeclaredProperties)
{ {


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

@@ -179,6 +179,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

@@ -16,14 +16,14 @@ namespace Discord
=> await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IDMChannel; => await client.GetPrivateChannelAsync(id).ConfigureAwait(false) as IDMChannel;
/// <summary> Gets all available DM channels for the client. </summary> /// <summary> Gets all available DM channels for the client. </summary>
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>();


/// <summary> Gets the group channel with the provided ID. </summary> /// <summary> Gets the group channel with the provided ID. </summary>
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;
/// <summary> Gets all available group channels for the client. </summary> /// <summary> Gets all available group channels for the client. </summary>
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>();


/// <summary> Gets the most optimal voice region for the client. </summary> /// <summary> Gets the most optimal voice region for the client. </summary>
public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this IDiscordClient discord) public static async Task<IVoiceRegion> GetOptimalVoiceRegionAsync(this IDiscordClient discord)


+ 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; }
} }
} }
}
}

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

@@ -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);


+ 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));
}
}

}
}

+ 19
- 4
src/Discord.Net.Rest/BaseDiscordClient.cs View File

@@ -55,17 +55,17 @@ namespace Discord.Rest
}; };
ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false); ApiClient.SentRequest += async (method, endpoint, millis) => await _restLogger.VerboseAsync($"{method} {endpoint}: {millis} ms").ConfigureAwait(false);
} }
public async Task LoginAsync(TokenType tokenType, string token)
public async Task LoginAsync(TokenType tokenType, string token, bool validateToken = true)
{ {
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)
{ {
@@ -79,6 +79,21 @@ 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;


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

@@ -1407,9 +1407,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
- 3
src/Discord.Net.Rest/Entities/Guilds/RestGuild.cs View File

@@ -277,7 +277,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();
} }


/// <summary> /// <summary>
@@ -306,7 +306,7 @@ 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();
} }


/// <summary> /// <summary>
@@ -320,7 +320,7 @@ namespace Discord.Rest
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();
} }


/// <summary> /// <summary>


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

@@ -133,14 +133,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; }


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

@@ -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;


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

@@ -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;




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

@@ -72,7 +72,7 @@ namespace Discord.WebSocket
switch (channel) switch (channel)
{ {
case SocketDMChannel dmChannel: case SocketDMChannel dmChannel:
_dmChannels.TryRemove(dmChannel.Recipient.Id, out var _);
_dmChannels.TryRemove(dmChannel.Recipient.Id, out _);
break; break;
case SocketGroupChannel _: case SocketGroupChannel _:
_groupChannels.TryRemove(id); _groupChannels.TryRemove(id);


+ 3
- 3
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 int[] _shardIds;
private readonly Dictionary<int, int> _shardIdsToIndex; private readonly Dictionary<int, int> _shardIdsToIndex;
private readonly bool _automaticShards;
private int[] _shardIds;
private DiscordSocketClient[] _shards; private DiscordSocketClient[] _shards;
private int _totalShards; private int _totalShards;
private readonly bool _automaticShards;
/// <inheritdoc /> /// <inheritdoc />
public override int Latency { get => GetLatency(); protected set { } } public override int Latency { get => GetLatency(); protected set { } }
@@ -26,7 +26,7 @@ namespace Discord.WebSocket
/// <inheritdoc /> /// <inheritdoc />
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;
internal new DiscordSocketApiClient ApiClient => base.ApiClient as DiscordSocketApiClient;
/// <inheritdoc /> /// <inheritdoc />
public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount); public override IReadOnlyCollection<SocketGuild> Guilds => GetGuilds().ToReadOnlyCollection(GetGuildCount);
/// <inheritdoc /> /// <inheritdoc />


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

@@ -25,9 +25,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;


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

@@ -83,7 +83,7 @@ namespace Discord.WebSocket
/// An collection of DM channels that have been opened in this session. /// An collection of DM channels that have been opened in this session.
/// </returns> /// </returns>
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();
/// <summary> /// <summary>
/// Gets a collection of group channels opened in this session. /// Gets a collection of group channels opened in this session.
/// </summary> /// </summary>
@@ -98,7 +98,7 @@ namespace Discord.WebSocket
/// An collection of group channels that have been opened in this session. /// An collection of group channels that have been opened in this session.
/// </returns> /// </returns>
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();
/// <inheritdoc /> /// <inheritdoc />
public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection(); public override IReadOnlyCollection<RestVoiceRegion> VoiceRegions => _voiceRegions.ToReadOnlyCollection();


@@ -1747,7 +1747,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);
} }


+ 1
- 1
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;




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

@@ -21,10 +21,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 readonly ConcurrentDictionary<ulong, SocketVoiceState> _voiceStates;


/// <inheritdoc /> /// <inheritdoc />
public string Name { get; private set; } public string Name { get; private set; }
@@ -153,7 +153,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);
@@ -167,7 +167,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

@@ -59,7 +59,7 @@ namespace Discord.WebSocket
=> ChannelHelper.ModifyAsync(this, Discord, func, options); => ChannelHelper.ModifyAsync(this, Discord, func, options);


/// <inheritdoc /> /// <inheritdoc />
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

@@ -171,7 +171,7 @@ namespace Discord.WebSocket
/// A read-only collection of message channels found within this guild. /// A read-only collection of message channels found within this guild.
/// </returns> /// </returns>
public IReadOnlyCollection<SocketTextChannel> TextChannels public IReadOnlyCollection<SocketTextChannel> TextChannels
=> Channels.Select(x => x as SocketTextChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketTextChannel>().ToImmutableArray();
/// <summary> /// <summary>
/// Gets a collection of all voice channels in this guild. /// Gets a collection of all voice channels in this guild.
/// </summary> /// </summary>
@@ -179,7 +179,7 @@ namespace Discord.WebSocket
/// A read-only collection of voice channels found within this guild. /// A read-only collection of voice channels found within this guild.
/// </returns> /// </returns>
public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels public IReadOnlyCollection<SocketVoiceChannel> VoiceChannels
=> Channels.Select(x => x as SocketVoiceChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketVoiceChannel>().ToImmutableArray();
/// <summary> /// <summary>
/// Gets a collection of all category channels in this guild. /// Gets a collection of all category channels in this guild.
/// </summary> /// </summary>
@@ -187,7 +187,7 @@ namespace Discord.WebSocket
/// A read-only collection of category channels found within this guild. /// A read-only collection of category channels found within this guild.
/// </returns> /// </returns>
public IReadOnlyCollection<SocketCategoryChannel> CategoryChannels public IReadOnlyCollection<SocketCategoryChannel> CategoryChannels
=> Channels.Select(x => x as SocketCategoryChannel).Where(x => x != null).ToImmutableArray();
=> Channels.OfType<SocketCategoryChannel>().ToImmutableArray();
/// <summary> /// <summary>
/// Gets the current logged-in user. /// Gets the current logged-in user.
/// </summary> /// </summary>
@@ -870,7 +870,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;
@@ -887,7 +887,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;


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

@@ -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 _);
_messages.TryRemove(msgId, out _);
} }
} }




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

@@ -15,13 +15,13 @@ 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 readonly List<SocketReaction> _reactions = new List<SocketReaction>();

/// <inheritdoc /> /// <inheritdoc />
public override bool IsTTS => _isTTS; public override bool IsTTS => _isTTS;
/// <inheritdoc /> /// <inheritdoc />


+ 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