@@ -8,9 +8,9 @@ namespace Discord.API | |||||
public ulong TargetId { get; set; } | public ulong TargetId { get; set; } | ||||
[JsonProperty("type")] | [JsonProperty("type")] | ||||
public PermissionTarget TargetType { get; set; } | public PermissionTarget TargetType { get; set; } | ||||
[JsonProperty("deny")] | |||||
[JsonProperty("deny"), Int53] | |||||
public ulong Deny { get; set; } | public ulong Deny { get; set; } | ||||
[JsonProperty("allow")] | |||||
[JsonProperty("allow"), Int53] | |||||
public ulong Allow { get; set; } | public ulong Allow { get; set; } | ||||
} | } | ||||
} | } |
@@ -14,7 +14,7 @@ namespace Discord.API | |||||
public bool? Hoist { get; set; } | public bool? Hoist { get; set; } | ||||
[JsonProperty("position")] | [JsonProperty("position")] | ||||
public int? Position { get; set; } | public int? Position { get; set; } | ||||
[JsonProperty("permissions")] | |||||
[JsonProperty("permissions"), Int53] | |||||
public ulong? Permissions { get; set; } | public ulong? Permissions { get; set; } | ||||
[JsonProperty("managed")] | [JsonProperty("managed")] | ||||
public bool? Managed { get; set; } | public bool? Managed { get; set; } | ||||
@@ -12,7 +12,7 @@ namespace Discord.API | |||||
public string Icon { get; set; } | public string Icon { get; set; } | ||||
[JsonProperty("owner")] | [JsonProperty("owner")] | ||||
public bool Owner { get; set; } | public bool Owner { get; set; } | ||||
[JsonProperty("permissions")] | |||||
public uint Permissions { get; set; } | |||||
[JsonProperty("permissions"), Int53] | |||||
public ulong Permissions { get; set; } | |||||
} | } | ||||
} | } |
@@ -29,7 +29,7 @@ namespace Discord.API | |||||
public TokenType AuthTokenType { get; private set; } | public TokenType AuthTokenType { get; private set; } | ||||
public IRestClient RestClient { get; private set; } | public IRestClient RestClient { get; private set; } | ||||
public IRequestQueue RequestQueue { get; private set; } | public IRequestQueue RequestQueue { get; private set; } | ||||
internal DiscordRawClient(RestClientProvider restClientProvider) | internal DiscordRawClient(RestClientProvider restClientProvider) | ||||
{ | { | ||||
_restClient = restClientProvider(DiscordConfig.ClientAPIUrl); | _restClient = restClientProvider(DiscordConfig.ClientAPIUrl); | ||||
@@ -38,18 +38,10 @@ namespace Discord.API | |||||
_requestQueue = new RequestQueue(_restClient); | _requestQueue = new RequestQueue(_restClient); | ||||
_serializer = new JsonSerializer(); | |||||
_serializer.Converters.Add(new OptionalConverter()); | |||||
_serializer.Converters.Add(new ChannelTypeConverter()); | |||||
_serializer.Converters.Add(new ImageConverter()); | |||||
_serializer.Converters.Add(new NullableUInt64Converter()); | |||||
_serializer.Converters.Add(new PermissionTargetConverter()); | |||||
_serializer.Converters.Add(new StringEntityConverter()); | |||||
_serializer.Converters.Add(new UInt64ArrayConverter()); | |||||
_serializer.Converters.Add(new UInt64Converter()); | |||||
_serializer.Converters.Add(new UInt64EntityConverter()); | |||||
_serializer.Converters.Add(new UserStatusConverter()); | |||||
_serializer.ContractResolver = new OptionalContractResolver(); | |||||
_serializer = new JsonSerializer() | |||||
{ | |||||
ContractResolver = new DiscordContractResolver() | |||||
}; | |||||
} | } | ||||
public async Task Login(TokenType tokenType, string token, CancellationToken cancelToken) | public async Task Login(TokenType tokenType, string token, CancellationToken cancelToken) | ||||
@@ -202,7 +194,7 @@ namespace Discord.API | |||||
{ | { | ||||
if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | ||||
return await Send<IEnumerable<Channel>>("GET", $"guild/{guildId}/channels").ConfigureAwait(false); | |||||
return await Send<IEnumerable<Channel>>("GET", $"guilds/{guildId}/channels").ConfigureAwait(false); | |||||
} | } | ||||
public async Task<Channel> CreateGuildChannel(ulong guildId, CreateGuildChannelParams args) | public async Task<Channel> CreateGuildChannel(ulong guildId, CreateGuildChannelParams args) | ||||
{ | { | ||||
@@ -539,7 +531,7 @@ namespace Discord.API | |||||
{ | { | ||||
if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | ||||
return await Send<IEnumerable<Role>>("GET", $"guild/{guildId}/roles").ConfigureAwait(false); | |||||
return await Send<IEnumerable<Role>>("GET", $"guilds/{guildId}/roles").ConfigureAwait(false); | |||||
} | } | ||||
public async Task<Role> CreateGuildRole(ulong guildId) | public async Task<Role> CreateGuildRole(ulong guildId) | ||||
{ | { | ||||
@@ -0,0 +1,7 @@ | |||||
using System; | |||||
namespace Discord.API | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Property)] | |||||
public class ImageAttribute : Attribute { } | |||||
} |
@@ -0,0 +1,7 @@ | |||||
using System; | |||||
namespace Discord.API | |||||
{ | |||||
[AttributeUsage(AttributeTargets.Property)] | |||||
public class Int53Attribute : Attribute { } | |||||
} |
@@ -11,7 +11,7 @@ namespace Discord.API.Rest | |||||
[JsonProperty("region")] | [JsonProperty("region")] | ||||
public string Region { get; set; } | public string Region { get; set; } | ||||
[JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | |||||
[JsonProperty("icon"), Image] | |||||
public Optional<Stream> Icon { get; set; } | public Optional<Stream> Icon { get; set; } | ||||
} | } | ||||
} | } |
@@ -14,7 +14,7 @@ namespace Discord.API.Rest | |||||
public Optional<string> Password { get; set; } | public Optional<string> Password { get; set; } | ||||
[JsonProperty("new_password")] | [JsonProperty("new_password")] | ||||
public Optional<string> NewPassword { get; set; } | public Optional<string> NewPassword { get; set; } | ||||
[JsonProperty("avatar"), JsonConverter(typeof(ImageConverter))] | |||||
[JsonProperty("avatar"), Image] | |||||
public Optional<Stream> Avatar { get; set; } | public Optional<Stream> Avatar { get; set; } | ||||
} | } | ||||
} | } |
@@ -16,11 +16,11 @@ namespace Discord.API.Rest | |||||
public Optional<ulong?> AFKChannelId { get; set; } | public Optional<ulong?> AFKChannelId { get; set; } | ||||
[JsonProperty("afk_timeout")] | [JsonProperty("afk_timeout")] | ||||
public Optional<int> AFKTimeout { get; set; } | public Optional<int> AFKTimeout { get; set; } | ||||
[JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | |||||
[JsonProperty("icon"), Image] | |||||
public Optional<Stream> Icon { get; set; } | public Optional<Stream> Icon { get; set; } | ||||
[JsonProperty("owner_id")] | [JsonProperty("owner_id")] | ||||
public Optional<GuildMember> Owner { get; set; } | public Optional<GuildMember> Owner { get; set; } | ||||
[JsonProperty("splash"), JsonConverter(typeof(ImageConverter))] | |||||
[JsonProperty("splash"), Image] | |||||
public Optional<Stream> Splash { get; set; } | public Optional<Stream> Splash { get; set; } | ||||
} | } | ||||
} | } |
@@ -67,6 +67,7 @@ | |||||
<Compile Include="API\Common\UserGuild.cs" /> | <Compile Include="API\Common\UserGuild.cs" /> | ||||
<Compile Include="API\Common\VoiceRegion.cs" /> | <Compile Include="API\Common\VoiceRegion.cs" /> | ||||
<Compile Include="API\Common\VoiceState.cs" /> | <Compile Include="API\Common\VoiceState.cs" /> | ||||
<Compile Include="API\ImageAttribute.cs" /> | |||||
<Compile Include="API\IOptional.cs" /> | <Compile Include="API\IOptional.cs" /> | ||||
<Compile Include="API\IWebSocketMessage.cs" /> | <Compile Include="API\IWebSocketMessage.cs" /> | ||||
<Compile Include="API\Optional.cs" /> | <Compile Include="API\Optional.cs" /> | ||||
@@ -105,7 +106,8 @@ | |||||
<Compile Include="Common\Entities\Users\StreamType.cs" /> | <Compile Include="Common\Entities\Users\StreamType.cs" /> | ||||
<Compile Include="DiscordConfig.cs" /> | <Compile Include="DiscordConfig.cs" /> | ||||
<Compile Include="API\DiscordRawClient.cs" /> | <Compile Include="API\DiscordRawClient.cs" /> | ||||
<Compile Include="Net\Converters\OptionalContractResolver.cs" /> | |||||
<Compile Include="API\Int53Attribute.cs" /> | |||||
<Compile Include="Net\Converters\DiscordContractResolver.cs" /> | |||||
<Compile Include="Net\Converters\OptionalConverter.cs" /> | <Compile Include="Net\Converters\OptionalConverter.cs" /> | ||||
<Compile Include="Net\RateLimitException.cs" /> | <Compile Include="Net\RateLimitException.cs" /> | ||||
<Compile Include="Net\Rest\RequestQueue\BucketGroup.cs" /> | <Compile Include="Net\Rest\RequestQueue\BucketGroup.cs" /> | ||||
@@ -5,7 +5,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class ChannelTypeConverter : JsonConverter | public class ChannelTypeConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(ChannelType); | |||||
public static readonly ChannelTypeConverter Instance = new ChannelTypeConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -0,0 +1,65 @@ | |||||
using Discord.API; | |||||
using Newtonsoft.Json; | |||||
using Newtonsoft.Json.Serialization; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq.Expressions; | |||||
using System.Reflection; | |||||
namespace Discord.Net.Converters | |||||
{ | |||||
public class DiscordContractResolver : DefaultContractResolver | |||||
{ | |||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | |||||
{ | |||||
var property = base.CreateProperty(member, memberSerialization); | |||||
var type = property.PropertyType; | |||||
JsonConverter converter = null; | |||||
if (member.MemberType == MemberTypes.Property) | |||||
{ | |||||
//Primitives | |||||
if (type == typeof(ulong) && member.GetCustomAttribute<Int53Attribute>() == null) | |||||
converter = UInt64Converter.Instance; | |||||
else if (type == typeof(ulong?) && member.GetCustomAttribute<Int53Attribute>() == null) | |||||
converter = NullableUInt64Converter.Instance; | |||||
else if (typeof(IEnumerable<ulong[]>).IsAssignableFrom(type) && member.GetCustomAttribute<Int53Attribute>() == null) | |||||
converter = NullableUInt64Converter.Instance; | |||||
//Enums | |||||
else if (type == typeof(ChannelType)) | |||||
converter = ChannelTypeConverter.Instance; | |||||
else if (type == typeof(PermissionTarget)) | |||||
converter = PermissionTargetConverter.Instance; | |||||
else if (type == typeof(UserStatus)) | |||||
converter = UserStatusConverter.Instance; | |||||
//Entities | |||||
else if (typeof(IEntity<ulong>).IsAssignableFrom(type)) | |||||
converter = UInt64EntityConverter.Instance; | |||||
else if (typeof(IEntity<string>).IsAssignableFrom(type)) | |||||
converter = StringEntityConverter.Instance; | |||||
//Special | |||||
else if (type == typeof(string) && member.GetCustomAttribute<ImageAttribute>() != null) | |||||
converter = ImageConverter.Instance; | |||||
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) | |||||
{ | |||||
var parentArg = Expression.Parameter(typeof(object)); | |||||
var optional = Expression.Property(Expression.Convert(parentArg, property.DeclaringType), member as PropertyInfo); | |||||
var isSpecified = Expression.Property(optional, OptionalConverter.IsSpecifiedProperty); | |||||
var lambda = Expression.Lambda<Func<object, bool>>(isSpecified, parentArg).Compile(); | |||||
property.ShouldSerialize = x => lambda(x); | |||||
converter = OptionalConverter.Instance; | |||||
} | |||||
} | |||||
if (converter != null) | |||||
{ | |||||
property.Converter = converter; | |||||
property.MemberConverter = converter; | |||||
} | |||||
return property; | |||||
} | |||||
} | |||||
} |
@@ -7,7 +7,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class ImageConverter : JsonConverter | public class ImageConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(Stream) || objectType == typeof(Optional<Stream>); | |||||
public static readonly ImageConverter Instance = new ImageConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -6,7 +6,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class NullableUInt64Converter : JsonConverter | public class NullableUInt64Converter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(ulong?); | |||||
public static readonly NullableUInt64Converter Instance = new NullableUInt64Converter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -1,34 +0,0 @@ | |||||
using Discord.API; | |||||
using Newtonsoft.Json; | |||||
using Newtonsoft.Json.Serialization; | |||||
using System; | |||||
using System.Linq.Expressions; | |||||
using System.Reflection; | |||||
namespace Discord.Net.Converters | |||||
{ | |||||
public class OptionalContractResolver : DefaultContractResolver | |||||
{ | |||||
private static readonly PropertyInfo _isSpecified = typeof(IOptional).GetProperty(nameof(IOptional.IsSpecified)); | |||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | |||||
{ | |||||
var property = base.CreateProperty(member, memberSerialization); | |||||
var type = property.PropertyType; | |||||
if (member.MemberType == MemberTypes.Property) | |||||
{ | |||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>)) | |||||
{ | |||||
var parentArg = Expression.Parameter(typeof(object)); | |||||
var optional = Expression.Property(Expression.Convert(parentArg, property.DeclaringType), member as PropertyInfo); | |||||
var isSpecified = Expression.Property(optional, _isSpecified); | |||||
var lambda = Expression.Lambda<Func<object, bool>>(isSpecified, parentArg).Compile(); | |||||
property.ShouldSerialize = x => lambda(x); | |||||
} | |||||
} | |||||
return property; | |||||
} | |||||
} | |||||
} |
@@ -1,12 +1,16 @@ | |||||
using Discord.API; | using Discord.API; | ||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
using System.Reflection; | |||||
namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
{ | { | ||||
public class OptionalConverter : JsonConverter | public class OptionalConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Optional<>); | |||||
public static readonly OptionalConverter Instance = new OptionalConverter(); | |||||
internal static readonly PropertyInfo IsSpecifiedProperty = typeof(IOptional).GetProperty(nameof(IOptional.IsSpecified)); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => false; | public override bool CanRead => false; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -5,7 +5,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class PermissionTargetConverter : JsonConverter | public class PermissionTargetConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(PermissionTarget); | |||||
public static readonly PermissionTargetConverter Instance = new PermissionTargetConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -5,7 +5,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class StringEntityConverter : JsonConverter | public class StringEntityConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(IEntity<string>); | |||||
public static readonly StringEntityConverter Instance = new StringEntityConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => false; | public override bool CanRead => false; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -7,7 +7,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class UInt64ArrayConverter : JsonConverter | public class UInt64ArrayConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(IEnumerable<ulong[]>); | |||||
public static readonly UInt64ArrayConverter Instance = new UInt64ArrayConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -6,7 +6,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class UInt64Converter : JsonConverter | public class UInt64Converter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(ulong); | |||||
public static readonly UInt64Converter Instance = new UInt64Converter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -1,11 +1,14 @@ | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
using System.Globalization; | |||||
namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
{ | { | ||||
public class UInt64EntityConverter : JsonConverter | public class UInt64EntityConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(IEntity<ulong>); | |||||
public static readonly UInt64EntityConverter Instance = new UInt64EntityConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => false; | public override bool CanRead => false; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
@@ -17,7 +20,7 @@ namespace Discord.Net.Converters | |||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | ||||
{ | { | ||||
if (value != null) | if (value != null) | ||||
writer.WriteValue((value as IEntity<ulong>).Id); | |||||
writer.WriteValue((value as IEntity<ulong>).Id.ToString(CultureInfo.InvariantCulture)); | |||||
else | else | ||||
writer.WriteNull(); | writer.WriteNull(); | ||||
} | } | ||||
@@ -5,7 +5,9 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
public class UserStatusConverter : JsonConverter | public class UserStatusConverter : JsonConverter | ||||
{ | { | ||||
public override bool CanConvert(Type objectType) => objectType == typeof(UserStatus); | |||||
public static readonly UserStatusConverter Instance = new UserStatusConverter(); | |||||
public override bool CanConvert(Type objectType) => true; | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => true; | public override bool CanWrite => true; | ||||