* Initial error parsing * Implement better errors * Add missing error codes * Add voice disconnect opcodes * Remove unused class, add summaries to discordjsonerror, and remove public constructor of slash command properties * Add error code summary * Update error message summary * Update src/Discord.Net.Core/DiscordJsonError.cs Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com> * Update src/Discord.Net.WebSocket/API/Voice/VoiceCloseCode.cs Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com> * Fix autocomplete result value Co-authored-by: Jared L <48422312+lhjt@users.noreply.github.com>pull/1923/head
| @@ -0,0 +1,197 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a set of json error codes received by discord. | |||||
| /// </summary> | |||||
| public enum DiscordErrorCode | |||||
| { | |||||
| GeneralError = 0, | |||||
| #region UnknownXYZ (10XXX) | |||||
| UnknownAccount = 10001, | |||||
| UnknownApplication = 10002, | |||||
| UnknownChannel = 10003, | |||||
| UnknownGuild = 10004, | |||||
| UnknownIntegration = 10005, | |||||
| UnknownInvite = 10006, | |||||
| UnknownMember = 10007, | |||||
| UnknownMessage = 10008, | |||||
| UnknownPermissionOverwrite = 10009, | |||||
| UnknownProvider = 10010, | |||||
| UnknownRole = 10011, | |||||
| UnknownToken = 10012, | |||||
| UnknownUser = 10013, | |||||
| UnknownEmoji = 10014, | |||||
| UnknownWebhook = 10015, | |||||
| UnknownWebhookService = 10016, | |||||
| UnknownSession = 10020, | |||||
| UnknownBan = 10026, | |||||
| UnknownSKU = 10027, | |||||
| UnknownStoreListing = 10028, | |||||
| UnknownEntitlement = 10029, | |||||
| UnknownBuild = 10030, | |||||
| UnknownLobby = 10031, | |||||
| UnknownBranch = 10032, | |||||
| UnknownStoreDirectoryLayout = 10033, | |||||
| UnknownRedistributable = 10036, | |||||
| UnknownGiftCode = 10038, | |||||
| UnknownStream = 10049, | |||||
| UnknownPremiumServerSubscribeCooldown = 10050, | |||||
| UnknownGuildTemplate = 10057, | |||||
| UnknownDiscoverableServerCategory = 10059, | |||||
| UnknownSticker = 10060, | |||||
| UnknownInteraction = 10062, | |||||
| UnknownApplicationCommand = 10063, | |||||
| UnknownApplicationCommandPermissions = 10066, | |||||
| UnknownStageInstance = 10067, | |||||
| UnknownGuildMemberVerificationForm = 10068, | |||||
| UnknownGuildWelcomeScreen = 10069, | |||||
| UnknownGuildScheduledEvent = 10070, | |||||
| UnknownGuildScheduledEventUser = 10071, | |||||
| #endregion | |||||
| #region General Actions (20XXX) | |||||
| BotsCannotUse = 20001, | |||||
| OnlyBotsCanUse = 20002, | |||||
| CannotSendExplicitContent = 20009, | |||||
| ApplicationActionUnauthorized = 20012, | |||||
| ActionSlowmode = 20016, | |||||
| OnlyOwnerAction = 20018, | |||||
| AnnouncementEditRatelimit = 20022, | |||||
| ChannelWriteRatelimit = 20028, | |||||
| WordsNotAllowed = 20031, | |||||
| GuildPremiumTooLow = 20035, | |||||
| #endregion | |||||
| #region Numeric Limits Reached (30XXX) | |||||
| MaximumGuildsReached = 30001, | |||||
| MaximumFriendsReached = 30002, | |||||
| MaximumPinsReached = 30003, | |||||
| MaximumRecipientsReached = 30004, | |||||
| MaximumGuildRolesReached = 30005, | |||||
| MaximumWebhooksReached = 30007, | |||||
| MaximumEmojisReached = 30008, | |||||
| MaximumReactionsReached = 30010, | |||||
| MaximumGuildChannelsReached = 30013, | |||||
| MaximumAttachmentsReached = 30015, | |||||
| MaximumInvitesReached = 30016, | |||||
| MaximumAnimatedEmojisReached = 30018, | |||||
| MaximumServerMembersReached = 30019, | |||||
| MaximumServerCategoriesReached = 30030, | |||||
| GuildTemplateAlreadyExists = 30031, | |||||
| MaximumThreadMembersReached = 30033, | |||||
| MaximumBansForNonGuildMembersReached = 30035, | |||||
| MaximumBanFetchesReached = 30037, | |||||
| MaximumUncompleteGuildScheduledEvents = 30038, | |||||
| MaximumStickersReached = 30039, | |||||
| MaximumPruneRequestReached = 30040, | |||||
| MaximumGuildWigitsReached = 30042, | |||||
| #endregion | |||||
| #region General Request Errors (40XXX) | |||||
| TokenUnauthorized = 40001, | |||||
| InvalidVerification = 40002, | |||||
| OpeningDMTooFast = 40003, | |||||
| RequestEntityTooLarge = 40005, | |||||
| FeatureDisabled = 40006, | |||||
| UserBanned = 40007, | |||||
| TargetUserNotInVoice = 40032, | |||||
| MessageAlreadyCrossposted = 40033, | |||||
| ApplicationNameAlreadyExists = 40041, | |||||
| #endregion | |||||
| #region Action Preconditions/Checks (50XXX) | |||||
| MissingPermissions = 50001, | |||||
| InvalidAccountType = 50002, | |||||
| CannotExecuteForDM = 50003, | |||||
| GuildWigitDisabled = 50004, | |||||
| CannotEditOtherUsersMessage = 50005, | |||||
| CannotSendEmptyMessage = 50006, | |||||
| CannotSendMessageToUser = 50007, | |||||
| CannotSendMessageToVoiceChannel = 50008, | |||||
| ChannelVerificationTooHight = 50009, | |||||
| OAuth2ApplicationDoesntHaveBot = 50010, | |||||
| OAuth2ApplicationLimitReached = 50011, | |||||
| InvalidOAuth2State = 50012, | |||||
| InsufficientPermissions = 50013, | |||||
| InvalidAuthenticationToken = 50014, | |||||
| NoteTooLong = 50015, | |||||
| ProvidedMessageDeleteCountOutOfBounds = 50016, | |||||
| InvalidPinChannel = 50019, | |||||
| InvalidInvite = 50020, | |||||
| CannotExecuteOnSystemMessage = 50021, | |||||
| CannotExecuteOnChannelType = 50024, | |||||
| InvalidOAuth2Token = 50025, | |||||
| MissingOAuth2Scope = 50026, | |||||
| InvalidWebhookToken = 50027, | |||||
| InvalidRole = 50028, | |||||
| InvalidRecipients = 50033, | |||||
| BulkDeleteMessageTooOld = 50034, | |||||
| InvalidFormBody = 50035, | |||||
| InviteAcceptedForGuildThatBotIsntIn = 50036, | |||||
| InvalidAPIVersion = 50041, | |||||
| FileUploadTooBig = 50045, | |||||
| InvalidFileUpload = 50046, | |||||
| CannotSelfRedeemGift = 50054, | |||||
| PaymentSourceRequiredForGift = 50070, | |||||
| CannotDeleteRequiredCommunityChannel = 50074, | |||||
| InvalidSticker = 50081, | |||||
| CannotExecuteOnArchivedThread = 50083, | |||||
| InvalidThreadNotificationSettings = 50084, | |||||
| BeforeValueEarlierThanThreadCreation = 50085, | |||||
| ServerLocaleUnavailable = 50095, | |||||
| ServerRequiresMonetization = 50097, | |||||
| ServerRequiresBoosts = 50101, | |||||
| #endregion | |||||
| #region 2FA (60XXX) | |||||
| Requires2FA = 60003, | |||||
| #endregion | |||||
| #region User Searches (80XXX) | |||||
| NoUsersWithTag = 80004, | |||||
| #endregion | |||||
| #region Reactions (90XXX) | |||||
| ReactionBlocked = 90001, | |||||
| #endregion | |||||
| #region API Status (130XXX) | |||||
| APIOverloaded = 130000, | |||||
| #endregion | |||||
| #region Stage Errors (150XXX) | |||||
| StageAlreadyOpened = 150006, | |||||
| #endregion | |||||
| #region Reply and Thread Errors (160XXX) | |||||
| CannotReplyWithoutReadMessageHistory = 160002, | |||||
| MessageAlreadyContainsThread = 160004, | |||||
| ThreadIsLocked = 160005, | |||||
| MaximumActiveThreadsReached = 160006, | |||||
| MaximumAnnouncementThreadsReached = 160007, | |||||
| #endregion | |||||
| #region Sticker Uploads (170XXX) | |||||
| InvalidJSONLottie = 170001, | |||||
| LottieCantContainRasters = 170002, | |||||
| StickerMaximumFramerateExceeded = 170003, | |||||
| StickerMaximumFrameCountExceeded = 170004, | |||||
| LottieMaximumDimentionsExceeded = 170005, | |||||
| StickerFramerateBoundsExceeed = 170006, | |||||
| StickerAnimationDurationTooLong = 170007, | |||||
| #endregion | |||||
| #region Guild Scheduled Events | |||||
| CannotUpdateFinishedEvent = 180000, | |||||
| FailedStageCreation = 180002, | |||||
| #endregion | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,53 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents a generic parsed json error received from discord after performing a rest request. | |||||
| /// </summary> | |||||
| public struct DiscordJsonError | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the json path of the error. | |||||
| /// </summary> | |||||
| public string Path { get; } | |||||
| /// <summary> | |||||
| /// Gets a collection of errors associated with the specific property at the path. | |||||
| /// </summary> | |||||
| public IReadOnlyCollection<DiscordError> Errors { get; } | |||||
| internal DiscordJsonError(string path, DiscordError[] errors) | |||||
| { | |||||
| Path = path; | |||||
| Errors = errors.ToImmutableArray(); | |||||
| } | |||||
| } | |||||
| /// <summary> | |||||
| /// Represents an error with a property. | |||||
| /// </summary> | |||||
| public struct DiscordError | |||||
| { | |||||
| /// <summary> | |||||
| /// Gets the code of the error. | |||||
| /// </summary> | |||||
| public string Code { get; } | |||||
| /// <summary> | |||||
| /// Gets the message describing what went wrong. | |||||
| /// </summary> | |||||
| public string Message { get; } | |||||
| internal DiscordError(string code, string message) | |||||
| { | |||||
| Code = code; | |||||
| Message = message; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,60 +1,15 @@ | |||||
| using System; | using System; | ||||
| using System.Linq; | |||||
| namespace Discord.Net | namespace Discord.Net | ||||
| { | { | ||||
| public class ApplicationCommandException : Exception | |||||
| [Obsolete("Please use HttpException instead of this. Will be removed in next major version.", false)] | |||||
| public class ApplicationCommandException : HttpException | |||||
| { | { | ||||
| /// <summary> | |||||
| /// Gets the JSON error code returned by Discord. | |||||
| /// </summary> | |||||
| /// <returns> | |||||
| /// A | |||||
| /// <see href="https://discord.com/developers/docs/topics/opcodes-and-status-codes#json">JSON error code</see> | |||||
| /// from Discord, or <c>null</c> if none. | |||||
| /// </returns> | |||||
| public int? DiscordCode { get; } | |||||
| /// <summary> | |||||
| /// Gets the reason of the exception. | |||||
| /// </summary> | |||||
| public string Reason { get; } | |||||
| /// <summary> | |||||
| /// Gets the request object used to send the request. | |||||
| /// </summary> | |||||
| public IRequest Request { get; } | |||||
| /// <summary> | |||||
| /// The error object returned from discord. | |||||
| /// </summary> | |||||
| /// <remarks> | |||||
| /// Note: This object can be null if discord didn't provide it. | |||||
| /// </remarks> | |||||
| public object Error { get; } | |||||
| /// <summary> | |||||
| /// The request json used to create the application command. This is useful for checking your commands for any format errors. | |||||
| /// </summary> | |||||
| public string RequestJson { get; } | |||||
| /// <summary> | |||||
| /// The underlying <see cref="HttpException"/> that caused this exception to be thrown. | |||||
| /// </summary> | |||||
| public HttpException InnerHttpException { get; } | |||||
| /// <summary> | |||||
| /// Initializes a new instance of the <see cref="ApplicationCommandException" /> class. | |||||
| /// </summary> | |||||
| /// <param name="requestJson"></param> | |||||
| /// <param name="httpError"></param> | |||||
| public ApplicationCommandException(string requestJson, HttpException httpError) | |||||
| : base("The application command failed to be created!", httpError) | |||||
| public ApplicationCommandException(HttpException httpError) | |||||
| : base(httpError.HttpCode, httpError.Request, httpError.DiscordCode, httpError.Reason, httpError.Errors.ToArray()) | |||||
| { | { | ||||
| Request = httpError.Request; | |||||
| DiscordCode = httpError.DiscordCode; | |||||
| Reason = httpError.Reason; | |||||
| Error = httpError.Error; | |||||
| RequestJson = requestJson; | |||||
| InnerHttpException = httpError; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,6 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | |||||
| using System.Collections.Immutable; | |||||
| using System.Net; | using System.Net; | ||||
| namespace Discord.Net | namespace Discord.Net | ||||
| @@ -25,7 +27,7 @@ namespace Discord.Net | |||||
| /// <see href="https://discord.com/developers/docs/topics/opcodes-and-status-codes#json">JSON error code</see> | /// <see href="https://discord.com/developers/docs/topics/opcodes-and-status-codes#json">JSON error code</see> | ||||
| /// from Discord, or <c>null</c> if none. | /// from Discord, or <c>null</c> if none. | ||||
| /// </returns> | /// </returns> | ||||
| public int? DiscordCode { get; } | |||||
| public DiscordErrorCode? DiscordCode { get; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Gets the reason of the exception. | /// Gets the reason of the exception. | ||||
| /// </summary> | /// </summary> | ||||
| @@ -35,12 +37,9 @@ namespace Discord.Net | |||||
| /// </summary> | /// </summary> | ||||
| public IRequest Request { get; } | public IRequest Request { get; } | ||||
| /// <summary> | /// <summary> | ||||
| /// The error object returned from discord. | |||||
| /// Gets a collection of json errors describing what went wrong with the request. | |||||
| /// </summary> | /// </summary> | ||||
| /// <remarks> | |||||
| /// Note: This object can be null if discord didn't provide it. | |||||
| /// </remarks> | |||||
| public object Error { get; } | |||||
| public IReadOnlyCollection<DiscordJsonError> Errors { get; } | |||||
| /// <summary> | /// <summary> | ||||
| /// Initializes a new instance of the <see cref="HttpException" /> class. | /// Initializes a new instance of the <see cref="HttpException" /> class. | ||||
| @@ -49,14 +48,14 @@ namespace Discord.Net | |||||
| /// <param name="request">The request that was sent prior to the exception.</param> | /// <param name="request">The request that was sent prior to the exception.</param> | ||||
| /// <param name="discordCode">The Discord status code returned.</param> | /// <param name="discordCode">The Discord status code returned.</param> | ||||
| /// <param name="reason">The reason behind the exception.</param> | /// <param name="reason">The reason behind the exception.</param> | ||||
| public HttpException(HttpStatusCode httpCode, IRequest request, int? discordCode = null, string reason = null, object errors = null) | |||||
| : base(CreateMessage(httpCode, discordCode, reason)) | |||||
| public HttpException(HttpStatusCode httpCode, IRequest request, DiscordErrorCode? discordCode = null, string reason = null, DiscordJsonError[] errors = null) | |||||
| : base(CreateMessage(httpCode, (int?)discordCode, reason)) | |||||
| { | { | ||||
| HttpCode = httpCode; | HttpCode = httpCode; | ||||
| Request = request; | Request = request; | ||||
| DiscordCode = discordCode; | DiscordCode = discordCode; | ||||
| Reason = reason; | Reason = reason; | ||||
| Error = errors; | |||||
| Errors = errors?.ToImmutableArray() ?? ImmutableArray<DiscordJsonError>.Empty; | |||||
| } | } | ||||
| private static string CreateMessage(HttpStatusCode httpCode, int? discordCode = null, string reason = null) | private static string CreateMessage(HttpStatusCode httpCode, int? discordCode = null, string reason = null) | ||||
| @@ -0,0 +1,20 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| [JsonConverter(typeof(Discord.Net.Converters.DiscordErrorConverter))] | |||||
| internal class DiscordError | |||||
| { | |||||
| [JsonProperty("message")] | |||||
| public string Message { get; set; } | |||||
| [JsonProperty("code")] | |||||
| public DiscordErrorCode Code { get; set; } | |||||
| [JsonProperty("errors")] | |||||
| public Optional<ErrorDetails[]> Errors { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,12 @@ | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class Error | |||||
| { | |||||
| [JsonProperty("code")] | |||||
| public string Code { get; set; } | |||||
| [JsonProperty("message")] | |||||
| public string Message { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,17 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API | |||||
| { | |||||
| internal class ErrorDetails | |||||
| { | |||||
| [JsonProperty("name")] | |||||
| public Optional<string> Name { get; set; } | |||||
| [JsonProperty("errors")] | |||||
| public Error[] Errors { get; set; } | |||||
| } | |||||
| } | |||||
| @@ -2235,7 +2235,7 @@ namespace Discord.API | |||||
| if (x.HttpCode == HttpStatusCode.BadRequest) | if (x.HttpCode == HttpStatusCode.BadRequest) | ||||
| { | { | ||||
| var json = (x.Request as JsonRestRequest).Json; | var json = (x.Request as JsonRestRequest).Json; | ||||
| throw new ApplicationCommandException(json, x); | |||||
| throw new ApplicationCommandException(x); | |||||
| } | } | ||||
| } | } | ||||
| @@ -2249,7 +2249,7 @@ namespace Discord.API | |||||
| if (x.HttpCode == HttpStatusCode.BadRequest) | if (x.HttpCode == HttpStatusCode.BadRequest) | ||||
| { | { | ||||
| var json = (x.Request as JsonRestRequest).Json; | var json = (x.Request as JsonRestRequest).Json; | ||||
| throw new ApplicationCommandException(json, x); | |||||
| throw new ApplicationCommandException(x); | |||||
| } | } | ||||
| throw; | throw; | ||||
| @@ -12,8 +12,8 @@ namespace Discord.Net.Converters | |||||
| { | { | ||||
| #region DiscordContractResolver | #region DiscordContractResolver | ||||
| private static readonly TypeInfo _ienumerable = typeof(IEnumerable<ulong[]>).GetTypeInfo(); | private static readonly TypeInfo _ienumerable = typeof(IEnumerable<ulong[]>).GetTypeInfo(); | ||||
| private static readonly MethodInfo _shouldSerialize = typeof(DiscordContractResolver).GetTypeInfo().GetDeclaredMethod("ShouldSerialize"); | |||||
| private static readonly MethodInfo _shouldSerialize = typeof(DiscordContractResolver).GetTypeInfo().GetDeclaredMethod("ShouldSerialize"); | |||||
| protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) | ||||
| { | { | ||||
| var property = base.CreateProperty(member, memberSerialization); | var property = base.CreateProperty(member, memberSerialization); | ||||
| @@ -58,7 +58,7 @@ namespace Discord.Net.Converters | |||||
| else if (genericType == typeof(EntityOrId<>)) | else if (genericType == typeof(EntityOrId<>)) | ||||
| return MakeGenericConverter(property, propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0], depth); | return MakeGenericConverter(property, propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0], depth); | ||||
| } | } | ||||
| #endregion | |||||
| #endregion | |||||
| #region Primitives | #region Primitives | ||||
| bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null; | bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null; | ||||
| @@ -87,6 +87,8 @@ namespace Discord.Net.Converters | |||||
| return MessageComponentConverter.Instance; | return MessageComponentConverter.Instance; | ||||
| if (type == typeof(API.Interaction)) | if (type == typeof(API.Interaction)) | ||||
| return InteractionConverter.Instance; | return InteractionConverter.Instance; | ||||
| if (type == typeof(API.DiscordError)) | |||||
| return DiscordErrorConverter.Instance; | |||||
| if (type == typeof(GuildFeatures)) | if (type == typeof(GuildFeatures)) | ||||
| return GuildFeaturesConverter.Instance; | return GuildFeaturesConverter.Instance; | ||||
| @@ -0,0 +1,88 @@ | |||||
| using Discord.API; | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json.Linq; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.Net.Converters | |||||
| { | |||||
| internal class DiscordErrorConverter : JsonConverter | |||||
| { | |||||
| public static DiscordErrorConverter Instance | |||||
| => new DiscordErrorConverter(); | |||||
| public override bool CanConvert(Type objectType) => objectType == typeof(DiscordError); | |||||
| public override bool CanRead => true; | |||||
| public override bool CanWrite => false; | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
| { | |||||
| var obj = JObject.Load(reader); | |||||
| var err = new API.DiscordError(); | |||||
| var result = obj.GetValue("errors", StringComparison.OrdinalIgnoreCase); | |||||
| result?.Parent.Remove(); | |||||
| // Populate the remaining properties. | |||||
| using (var subReader = obj.CreateReader()) | |||||
| { | |||||
| serializer.Populate(subReader, err); | |||||
| } | |||||
| if (result != null) | |||||
| { | |||||
| var innerReader = result.CreateReader(); | |||||
| var errors = ReadErrors(innerReader); | |||||
| err.Errors = errors.ToArray(); | |||||
| } | |||||
| return err; | |||||
| } | |||||
| private List<ErrorDetails> ReadErrors(JsonReader reader, string path = "") | |||||
| { | |||||
| List<ErrorDetails> errs = new List<ErrorDetails>(); | |||||
| var obj = JObject.Load(reader); | |||||
| var props = obj.Properties(); | |||||
| foreach (var prop in props) | |||||
| { | |||||
| if (prop.Name == "_errors" && path == "") // root level error | |||||
| { | |||||
| errs.Add(new ErrorDetails() | |||||
| { | |||||
| Name = Optional<string>.Unspecified, | |||||
| Errors = prop.Value.ToObject<Error[]>() | |||||
| }); | |||||
| } | |||||
| else if (prop.Name == "_errors") // path errors (not root level) | |||||
| { | |||||
| errs.Add(new ErrorDetails() | |||||
| { | |||||
| Name = path, | |||||
| Errors = prop.Value.ToObject<Error[]>() | |||||
| }); | |||||
| } | |||||
| else if(int.TryParse(prop.Name, out var i)) // array value | |||||
| { | |||||
| var r = prop.Value.CreateReader(); | |||||
| errs.AddRange(ReadErrors(r, path + $"[{i}]")); | |||||
| } | |||||
| else // property name | |||||
| { | |||||
| var r = prop.Value.CreateReader(); | |||||
| errs.AddRange(ReadErrors(r, path + $"{(path != "" ? "." : "")}{prop.Name[0].ToString().ToUpper() + new string(prop.Name.Skip(1).ToArray())}")); | |||||
| } | |||||
| } | |||||
| return errs; | |||||
| } | |||||
| public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); | |||||
| } | |||||
| } | |||||
| @@ -1,3 +1,4 @@ | |||||
| using Discord.API; | |||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||
| using System; | using System; | ||||
| @@ -5,6 +6,7 @@ using System; | |||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| #endif | #endif | ||||
| using System.IO; | using System.IO; | ||||
| using System.Linq; | |||||
| using System.Net; | using System.Net; | ||||
| using System.Threading; | using System.Threading; | ||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| @@ -99,9 +101,7 @@ namespace Discord.Net.Queue | |||||
| continue; //Retry | continue; //Retry | ||||
| default: | default: | ||||
| int? code = null; | |||||
| string reason = null; | |||||
| object errors = null; | |||||
| API.DiscordError error = null; | |||||
| if (response.Stream != null) | if (response.Stream != null) | ||||
| { | { | ||||
| try | try | ||||
| @@ -109,15 +109,14 @@ namespace Discord.Net.Queue | |||||
| using (var reader = new StreamReader(response.Stream)) | using (var reader = new StreamReader(response.Stream)) | ||||
| using (var jsonReader = new JsonTextReader(reader)) | using (var jsonReader = new JsonTextReader(reader)) | ||||
| { | { | ||||
| var json = JToken.Load(jsonReader); | |||||
| try { code = json.Value<int>("code"); } catch { }; | |||||
| try { reason = json.Value<string>("message"); } catch { }; | |||||
| try { errors = json.Value<object>("errors"); } catch { }; | |||||
| error = Discord.Rest.DiscordRestClient.Serializer.Deserialize<API.DiscordError>(jsonReader); | |||||
| } | } | ||||
| } | } | ||||
| catch { } | catch { } | ||||
| } | } | ||||
| throw new HttpException(response.StatusCode, request, code, reason, errors); | |||||
| throw new HttpException(response.StatusCode, request, error?.Code, error.Message, error.Errors.IsSpecified | |||||
| ? error.Errors.Value.Select(x => new DiscordJsonError(x.Name.GetValueOrDefault("root"), x.Errors.Select(y => new DiscordError(y.Code, y.Message)).ToArray())).ToArray() | |||||
| : null); | |||||
| } | } | ||||
| } | } | ||||
| else | else | ||||
| @@ -0,0 +1,63 @@ | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace Discord.API.Voice | |||||
| { | |||||
| /// <summary> | |||||
| /// Represents generic op codes for voice disconnect. | |||||
| /// </summary> | |||||
| public enum VoiceCloseCode | |||||
| { | |||||
| /// <summary> | |||||
| /// You sent an invalid opcode. | |||||
| /// </summary> | |||||
| UnknownOpcode = 4001, | |||||
| /// <summary> | |||||
| /// You sent an invalid payload in your identifying to the Gateway. | |||||
| /// </summary> | |||||
| DecodeFailure = 4002, | |||||
| /// <summary> | |||||
| /// You sent a payload before identifying with the Gateway. | |||||
| /// </summary> | |||||
| NotAuthenticated = 4003, | |||||
| /// <summary> | |||||
| /// The token you sent in your identify payload is incorrect. | |||||
| /// </summary> | |||||
| AuthenticationFailed = 4004, | |||||
| /// <summary> | |||||
| /// You sent more than one identify payload. Stahp. | |||||
| /// </summary> | |||||
| AlreadyAuthenticated = 4005, | |||||
| /// <summary> | |||||
| /// Your session is no longer valid. | |||||
| /// </summary> | |||||
| SessionNolongerValid = 4006, | |||||
| /// <summary> | |||||
| /// Your session has timed out. | |||||
| /// </summary> | |||||
| SessionTimeout = 4009, | |||||
| /// <summary> | |||||
| /// We can't find the server you're trying to connect to. | |||||
| /// </summary> | |||||
| ServerNotFound = 4011, | |||||
| /// <summary> | |||||
| /// We didn't recognize the protocol you sent. | |||||
| /// </summary> | |||||
| UnknownProtocol = 4012, | |||||
| /// <summary> | |||||
| /// Channel was deleted, you were kicked, voice server changed, or the main gateway session was dropped. Should not reconnect. | |||||
| /// </summary> | |||||
| Disconnected = 4014, | |||||
| /// <summary> | |||||
| /// The server crashed. Our bad! Try resuming. | |||||
| /// </summary> | |||||
| VoiceServerCrashed = 4015, | |||||
| /// <summary> | |||||
| /// We didn't recognize your encryption. | |||||
| /// </summary> | |||||
| UnknownEncryptionMode = 4016, | |||||
| } | |||||
| } | |||||