@@ -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. |
@@ -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; | |||||
} | |||||
} | } | ||||
} | } |
@@ -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; | ||||
@@ -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; | ||||
} | } | ||||
@@ -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> | ||||
@@ -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; | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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); | ||||
@@ -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]; | |||||
} | } | ||||
} | } |
@@ -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")); | ||||
} | } | ||||
@@ -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) | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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 | ||||
@@ -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; | ||||
} | } | ||||
@@ -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; | |||||
} | } | ||||
} | } |
@@ -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; | |||||
} | } | ||||
} | } |
@@ -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; | |||||
} | } | ||||
} | } |
@@ -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); | ||||
@@ -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) | ||||
{ | { | ||||
@@ -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}**"; | ||||
@@ -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}"); | ||||
} | } | ||||
@@ -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; } | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -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)) | ||||
{ | { | ||||
@@ -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); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -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)); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -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; } | ||||
} | } | ||||
@@ -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; | ||||
@@ -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, | ||||
@@ -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++; | ||||
@@ -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))); | ||||
} | } | ||||
@@ -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()); | ||||
@@ -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); | ||||
@@ -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); | ||||
} | } | ||||
@@ -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); | ||||
} | } | ||||
@@ -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); | ||||
} | } | ||||
@@ -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) | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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); | ||||
@@ -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(); | ||||
@@ -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); | ||||
@@ -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; } | ||||
@@ -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; } | ||||
@@ -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; } | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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); | ||||
@@ -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); | ||||
} | } | ||||
@@ -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); | ||||
} | } | ||||
@@ -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/> | ||||
@@ -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; } | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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) | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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}"); | ||||
@@ -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,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 | ||||
} | } | ||||
@@ -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) && | ||||
@@ -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; | ||||
} | } | ||||
} | } | ||||
@@ -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); | ||||
} | } | ||||
} | } | ||||
} | |||||
} |
@@ -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); | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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); | ||||
} | } | ||||
@@ -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; | ||||
@@ -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; | ||||
} | } | ||||
@@ -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; | ||||
} | } | ||||
@@ -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); | ||||
} | } | ||||
@@ -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; | ||||
@@ -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 _); | |||||
} | } | ||||
} | } | ||||
@@ -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; | ||||
@@ -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; | ||||
@@ -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")); | |||||
} | |||||
} | |||||
} |