diff --git a/src/Discord.Net.Commands/CommandParser.cs b/src/Discord.Net.Commands/CommandParser.cs index c77b56042..88698cda5 100644 --- a/src/Discord.Net.Commands/CommandParser.cs +++ b/src/Discord.Net.Commands/CommandParser.cs @@ -103,7 +103,7 @@ namespace Discord.Commands argBuilder.Append(c); continue; } - + if (IsOpenQuote(aliasMap, c)) { curPart = ParserPart.QuotedParameter; @@ -136,7 +136,7 @@ namespace Discord.Commands else argBuilder.Append(c); } - + if (argString != null) { if (curParam == null) @@ -149,7 +149,7 @@ namespace Discord.Commands var typeReaderResult = await curParam.ParseAsync(context, argString, services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess && typeReaderResult.Error != CommandError.MultipleMatches) - return ParseResult.FromError(typeReaderResult); + return ParseResult.FromError(typeReaderResult, curParam); if (curParam.IsMultiple) { @@ -172,7 +172,7 @@ namespace Discord.Commands { var typeReaderResult = await curParam.ParseAsync(context, argBuilder.ToString(), services).ConfigureAwait(false); if (!typeReaderResult.IsSuccess) - return ParseResult.FromError(typeReaderResult); + return ParseResult.FromError(typeReaderResult, curParam); argList.Add(typeReaderResult); } @@ -180,7 +180,7 @@ namespace Discord.Commands return ParseResult.FromError(CommandError.ParseFailed, "Input text may not end on an incomplete escape."); if (curPart == ParserPart.QuotedParameter) return ParseResult.FromError(CommandError.ParseFailed, "A quoted parameter is incomplete."); - + //Add missing optionals for (int i = argList.Count; i < command.Parameters.Count; i++) { @@ -191,7 +191,7 @@ namespace Discord.Commands return ParseResult.FromError(CommandError.BadArgCount, "The input text has too few parameters."); argList.Add(TypeReaderResult.FromSuccess(param.DefaultValue)); } - + return ParseResult.FromSuccess(argList.ToImmutable(), paramList.ToImmutable()); } } diff --git a/src/Discord.Net.Commands/Results/ParseResult.cs b/src/Discord.Net.Commands/Results/ParseResult.cs index 8e10dc66c..43351b6bf 100644 --- a/src/Discord.Net.Commands/Results/ParseResult.cs +++ b/src/Discord.Net.Commands/Results/ParseResult.cs @@ -18,30 +18,40 @@ namespace Discord.Commands /// public string ErrorReason { get; } + /// + /// Provides information about the parameter that caused the parsing error. + /// + /// + /// A indicating the parameter info of the error that may have occurred during parsing; + /// null if the parsing was successful or the parsing error is not specific to a single parameter. + /// + public ParameterInfo ErrorParameter { get; } + /// public bool IsSuccess => !Error.HasValue; - private ParseResult(IReadOnlyList argValues, IReadOnlyList paramValues, CommandError? error, string errorReason) + private ParseResult(IReadOnlyList argValues, IReadOnlyList paramValues, CommandError? error, string errorReason, ParameterInfo errorParamInfo) { ArgValues = argValues; ParamValues = paramValues; Error = error; ErrorReason = errorReason; + ErrorParameter = errorParamInfo; } - + public static ParseResult FromSuccess(IReadOnlyList argValues, IReadOnlyList paramValues) { for (int i = 0; i < argValues.Count; i++) { if (argValues[i].Values.Count > 1) - return new ParseResult(argValues, paramValues, CommandError.MultipleMatches, "Multiple matches found."); + return new ParseResult(argValues, paramValues, CommandError.MultipleMatches, "Multiple matches found.", null); } for (int i = 0; i < paramValues.Count; i++) { if (paramValues[i].Values.Count > 1) - return new ParseResult(argValues, paramValues, CommandError.MultipleMatches, "Multiple matches found."); + return new ParseResult(argValues, paramValues, CommandError.MultipleMatches, "Multiple matches found.", null); } - return new ParseResult(argValues, paramValues, null, null); + return new ParseResult(argValues, paramValues, null, null, null); } public static ParseResult FromSuccess(IReadOnlyList argValues, IReadOnlyList paramValues) { @@ -55,15 +65,19 @@ namespace Discord.Commands for (int i = 0; i < paramValues.Count; i++) paramList[i] = TypeReaderResult.FromSuccess(paramValues[i]); } - return new ParseResult(argList, paramList, null, null); + return new ParseResult(argList, paramList, null, null, null); } public static ParseResult FromError(CommandError error, string reason) - => new ParseResult(null, null, error, reason); + => new ParseResult(null, null, error, reason, null); + public static ParseResult FromError(CommandError error, string reason, ParameterInfo parameterInfo) + => new ParseResult(null, null, error, reason, parameterInfo); public static ParseResult FromError(Exception ex) => FromError(CommandError.Exception, ex.Message); public static ParseResult FromError(IResult result) - => new ParseResult(null, null, result.Error, result.ErrorReason); + => new ParseResult(null, null, result.Error, result.ErrorReason, null); + public static ParseResult FromError(IResult result, ParameterInfo parameterInfo) + => new ParseResult(null, null, result.Error, result.ErrorReason, parameterInfo); public override string ToString() => IsSuccess ? "Success" : $"{Error}: {ErrorReason}"; private string DebuggerDisplay => IsSuccess ? $"Success ({ArgValues.Count}{(ParamValues.Count > 0 ? $" +{ParamValues.Count} Values" : "")})" : $"{Error}: {ErrorReason}"; diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs index e3cfc0e19..e1f78373e 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermission.cs @@ -90,6 +90,10 @@ namespace Discord /// UseVAD = 0x02_00_00_00, PrioritySpeaker = 0x00_00_01_00, + /// + /// Allows video streaming in a voice channel. + /// + Stream = 0x00_00_02_00, // More General /// diff --git a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs index 7bef3251a..99885b070 100644 --- a/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/ChannelPermissions.cs @@ -13,7 +13,7 @@ namespace Discord /// Gets a that grants all permissions for text channels. public static readonly ChannelPermissions Text = new ChannelPermissions(0b01100_0000000_1111111110001_010001); /// Gets a that grants all permissions for voice channels. - public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000010100_010001); + public static readonly ChannelPermissions Voice = new ChannelPermissions(0b00100_1111110_0000000011100_010001); /// Gets a that grants all permissions for category channels. public static readonly ChannelPermissions Category = new ChannelPermissions(0b01100_1111110_1111111110001_010001); /// Gets a that grants all permissions for direct message channels. @@ -82,6 +82,8 @@ namespace Discord public bool UseVAD => Permissions.GetValue(RawValue, ChannelPermission.UseVAD); /// If true, a user may use priority speaker in a voice channel. public bool PrioritySpeaker => Permissions.GetValue(RawValue, ChannelPermission.PrioritySpeaker); + /// If true, a user may stream video in a voice channel. + public bool Stream => Permissions.GetValue(RawValue, ChannelPermission.Stream); /// If true, a user may adjust role permissions. This also implictly grants all other permissions. public bool ManageRoles => Permissions.GetValue(RawValue, ChannelPermission.ManageRoles); @@ -111,6 +113,7 @@ namespace Discord bool? moveMembers = null, bool? useVoiceActivation = null, bool? prioritySpeaker = null, + bool? stream = null, bool? manageRoles = null, bool? manageWebhooks = null) { @@ -135,6 +138,7 @@ namespace Discord Permissions.SetValue(ref value, moveMembers, ChannelPermission.MoveMembers); Permissions.SetValue(ref value, useVoiceActivation, ChannelPermission.UseVAD); Permissions.SetValue(ref value, prioritySpeaker, ChannelPermission.PrioritySpeaker); + Permissions.SetValue(ref value, stream, ChannelPermission.Stream); Permissions.SetValue(ref value, manageRoles, ChannelPermission.ManageRoles); Permissions.SetValue(ref value, manageWebhooks, ChannelPermission.ManageWebhooks); @@ -162,11 +166,12 @@ namespace Discord bool moveMembers = false, bool useVoiceActivation = false, bool prioritySpeaker = false, + bool stream = false, bool manageRoles = false, bool manageWebhooks = false) : this(0, createInstantInvite, manageChannel, addReactions, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, - speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, prioritySpeaker, manageRoles, manageWebhooks) + speak, muteMembers, deafenMembers, moveMembers, useVoiceActivation, prioritySpeaker, stream, manageRoles, manageWebhooks) { } /// Creates a new from this one, changing the provided non-null permissions. @@ -190,6 +195,7 @@ namespace Discord bool? moveMembers = null, bool? useVoiceActivation = null, bool? prioritySpeaker = null, + bool? stream = null, bool? manageRoles = null, bool? manageWebhooks = null) => new ChannelPermissions(RawValue, @@ -212,6 +218,7 @@ namespace Discord moveMembers, useVoiceActivation, prioritySpeaker, + stream, manageRoles, manageWebhooks); diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs index c010d90b1..3c8a5e810 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermission.cs @@ -126,6 +126,10 @@ namespace Discord /// UseVAD = 0x02_00_00_00, PrioritySpeaker = 0x00_00_01_00, + /// + /// Allows video streaming in a voice channel. + /// + Stream = 0x00_00_02_00, // General 2 /// diff --git a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs index 2d2a9e56a..a5adad47c 100644 --- a/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs +++ b/src/Discord.Net.Core/Entities/Permissions/GuildPermissions.cs @@ -12,7 +12,7 @@ namespace Discord /// Gets a that grants all guild permissions for webhook users. public static readonly GuildPermissions Webhook = new GuildPermissions(0b00000_0000000_0001101100000_000000); /// Gets a that grants all guild permissions. - public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111110111_111111); + public static readonly GuildPermissions All = new GuildPermissions(0b11111_1111110_1111111111111_111111); /// Gets a packed value representing all the permissions in this . public ulong RawValue { get; } @@ -70,7 +70,9 @@ namespace Discord /// If true, a user may use voice-activity-detection rather than push-to-talk. public bool UseVAD => Permissions.GetValue(RawValue, GuildPermission.UseVAD); /// If True, a user may use priority speaker in a voice channel. - public bool PrioritySpeaker => Permissions.GetValue(RawValue, ChannelPermission.PrioritySpeaker); + public bool PrioritySpeaker => Permissions.GetValue(RawValue, GuildPermission.PrioritySpeaker); + /// If True, a user may stream video in a voice channel. + public bool Stream => Permissions.GetValue(RawValue, GuildPermission.Stream); /// If true, a user may change their own nickname. public bool ChangeNickname => Permissions.GetValue(RawValue, GuildPermission.ChangeNickname); @@ -111,6 +113,7 @@ namespace Discord bool? moveMembers = null, bool? useVoiceActivation = null, bool? prioritySpeaker = null, + bool? stream = null, bool? changeNickname = null, bool? manageNicknames = null, bool? manageRoles = null, @@ -143,6 +146,7 @@ namespace Discord Permissions.SetValue(ref value, moveMembers, GuildPermission.MoveMembers); Permissions.SetValue(ref value, useVoiceActivation, GuildPermission.UseVAD); Permissions.SetValue(ref value, prioritySpeaker, GuildPermission.PrioritySpeaker); + Permissions.SetValue(ref value, stream, GuildPermission.Stream); Permissions.SetValue(ref value, changeNickname, GuildPermission.ChangeNickname); Permissions.SetValue(ref value, manageNicknames, GuildPermission.ManageNicknames); Permissions.SetValue(ref value, manageRoles, GuildPermission.ManageRoles); @@ -178,6 +182,7 @@ namespace Discord bool moveMembers = false, bool useVoiceActivation = false, bool prioritySpeaker = false, + bool stream = false, bool changeNickname = false, bool manageNicknames = false, bool manageRoles = false, @@ -209,6 +214,7 @@ namespace Discord moveMembers: moveMembers, useVoiceActivation: useVoiceActivation, prioritySpeaker: prioritySpeaker, + stream: stream, changeNickname: changeNickname, manageNicknames: manageNicknames, manageWebhooks: manageWebhooks, @@ -241,6 +247,7 @@ namespace Discord bool? moveMembers = null, bool? useVoiceActivation = null, bool? prioritySpeaker = null, + bool? stream = null, bool? changeNickname = null, bool? manageNicknames = null, bool? manageRoles = null, @@ -249,7 +256,7 @@ namespace Discord => new GuildPermissions(RawValue, createInstantInvite, kickMembers, banMembers, administrator, manageChannels, manageGuild, addReactions, viewAuditLog, viewChannel, sendMessages, sendTTSMessages, manageMessages, embedLinks, attachFiles, readMessageHistory, mentionEveryone, useExternalEmojis, connect, speak, muteMembers, deafenMembers, moveMembers, - useVoiceActivation, prioritySpeaker, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); + useVoiceActivation, prioritySpeaker, stream, changeNickname, manageNicknames, manageRoles, manageWebhooks, manageEmojis); /// /// Returns a value that indicates if a specific is enabled diff --git a/src/Discord.Net.Core/Utils/TokenUtils.cs b/src/Discord.Net.Core/Utils/TokenUtils.cs index 2efb1822a..c3dd39237 100644 --- a/src/Discord.Net.Core/Utils/TokenUtils.cs +++ b/src/Discord.Net.Core/Utils/TokenUtils.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Text; namespace Discord @@ -76,7 +77,7 @@ namespace Discord var bytes = Convert.FromBase64String(encoded); var idStr = Encoding.UTF8.GetString(bytes); // try to parse a ulong from the resulting string - if (ulong.TryParse(idStr, out var id)) + if (ulong.TryParse(idStr, NumberStyles.None, CultureInfo.InvariantCulture, out var id)) return id; } catch (DecoderFallbackException) diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs index 30fc39000..8f3a47368 100644 --- a/src/Discord.Net.Rest/DiscordRestApiClient.cs +++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs @@ -1492,7 +1492,7 @@ namespace Discord.API builder.Append(format, lastIndex, leftIndex - lastIndex); int rightIndex = format.IndexOf("}", leftIndex); - int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1)); + int argId = int.Parse(format.Substring(leftIndex + 1, rightIndex - leftIndex - 1), NumberStyles.None, CultureInfo.InvariantCulture); string fieldName = GetFieldName(methodArgs[argId + 1]); var mappedId = BucketIds.GetIndex(fieldName); diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs index a692829f4..f432b4ca5 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelCreateAuditLogData.cs @@ -11,11 +11,14 @@ namespace Discord.Rest /// public class ChannelCreateAuditLogData : IAuditLogData { - private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) + private ChannelCreateAuditLogData(ulong id, string name, ChannelType type, int? rateLimit, bool? nsfw, int? bitrate, IReadOnlyCollection overwrites) { ChannelId = id; ChannelName = name; ChannelType = type; + SlowModeInterval = rateLimit; + IsNsfw = nsfw; + Bitrate = bitrate; Overwrites = overwrites; } @@ -27,9 +30,15 @@ namespace Discord.Rest var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites"); var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); + var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); + var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); var type = typeModel.NewValue.ToObject(discord.ApiClient.Serializer); var name = nameModel.NewValue.ToObject(discord.ApiClient.Serializer); + int? rateLimitPerUser = rateLimitPerUserModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + bool? nsfw = nsfwModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + int? bitrate = bitrateModel?.NewValue?.ToObject(discord.ApiClient.Serializer); foreach (var overwrite in overwritesModel.NewValue) { @@ -41,7 +50,7 @@ namespace Discord.Rest overwrites.Add(new Overwrite(id, permType, new OverwritePermissions(allow, deny))); } - return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, overwrites.ToReadOnlyCollection()); + return new ChannelCreateAuditLogData(entry.TargetId.Value, name, type, rateLimitPerUser, nsfw, bitrate, overwrites.ToReadOnlyCollection()); } /// @@ -66,6 +75,32 @@ namespace Discord.Rest /// public ChannelType ChannelType { get; } /// + /// Gets the current slow-mode delay of the created channel. + /// + /// + /// An representing the time in seconds required before the user can send another + /// message; 0 if disabled. + /// null if this is not mentioned in this entry. + /// + public int? SlowModeInterval { get; } + /// + /// Gets the value that indicates whether the created channel is NSFW. + /// + /// + /// true if the created channel has the NSFW flag enabled; otherwise false. + /// null if this is not mentioned in this entry. + /// + public bool? IsNsfw { get; } + /// + /// Gets the bit-rate that the clients in the created voice channel are requested to use. + /// + /// + /// An representing the bit-rate (bps) that the created voice channel defines and requests the + /// client(s) to use. + /// null if this is not mentioned in this entry. + /// + public int? Bitrate { get; } + /// /// Gets a collection of permission overwrites that was assigned to the created channel. /// /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs index d09b658cf..390749929 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelDeleteAuditLogData.cs @@ -11,11 +11,14 @@ namespace Discord.Rest /// public class ChannelDeleteAuditLogData : IAuditLogData { - private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, IReadOnlyCollection overwrites) + private ChannelDeleteAuditLogData(ulong id, string name, ChannelType type, int? rateLimit, bool? nsfw, int? bitrate, IReadOnlyCollection overwrites) { ChannelId = id; ChannelName = name; ChannelType = type; + SlowModeInterval = rateLimit; + IsNsfw = nsfw; + Bitrate = bitrate; Overwrites = overwrites; } @@ -26,15 +29,21 @@ namespace Discord.Rest var overwritesModel = changes.FirstOrDefault(x => x.ChangedProperty == "permission_overwrites"); var typeModel = changes.FirstOrDefault(x => x.ChangedProperty == "type"); var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); + var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); + var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); + var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); var overwrites = overwritesModel.OldValue.ToObject(discord.ApiClient.Serializer) .Select(x => new Overwrite(x.TargetId, x.TargetType, new OverwritePermissions(x.Allow, x.Deny))) .ToList(); var type = typeModel.OldValue.ToObject(discord.ApiClient.Serializer); var name = nameModel.OldValue.ToObject(discord.ApiClient.Serializer); + int? rateLimitPerUser = rateLimitPerUserModel?.OldValue?.ToObject(discord.ApiClient.Serializer); + bool? nsfw = nsfwModel?.OldValue?.ToObject(discord.ApiClient.Serializer); + int? bitrate = bitrateModel?.OldValue?.ToObject(discord.ApiClient.Serializer); var id = entry.TargetId.Value; - return new ChannelDeleteAuditLogData(id, name, type, overwrites.ToReadOnlyCollection()); + return new ChannelDeleteAuditLogData(id, name, type, rateLimitPerUser, nsfw, bitrate, overwrites.ToReadOnlyCollection()); } /// @@ -59,6 +68,31 @@ namespace Discord.Rest /// public ChannelType ChannelType { get; } /// + /// Gets the slow-mode delay of the deleted channel. + /// + /// + /// An representing the time in seconds required before the user can send another + /// message; 0 if disabled. + /// null if this is not mentioned in this entry. + /// + public int? SlowModeInterval { get; } + /// + /// Gets the value that indicates whether the deleted channel was NSFW. + /// + /// + /// true if this channel had the NSFW flag enabled; otherwise false. + /// null if this is not mentioned in this entry. + /// + public bool? IsNsfw { get; } + /// + /// Gets the bit-rate of this channel if applicable. + /// + /// + /// An representing the bit-rate set of the voice channel. + /// null if this is not mentioned in this entry. + /// + public int? Bitrate { get; } + /// /// Gets a collection of permission overwrites that was assigned to the deleted channel. /// /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs index 6382e8250..d6d2fb4b3 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelInfo.cs @@ -5,12 +5,13 @@ namespace Discord.Rest /// public struct ChannelInfo { - internal ChannelInfo(string name, string topic, int? bitrate, int? limit) + internal ChannelInfo(string name, string topic, int? rateLimit, bool? nsfw, int? bitrate) { Name = name; Topic = topic; + SlowModeInterval = rateLimit; + IsNsfw = nsfw; Bitrate = bitrate; - UserLimit = limit; } /// @@ -28,20 +29,29 @@ namespace Discord.Rest /// public string Topic { get; } /// - /// Gets the bit-rate of this channel if applicable. + /// Gets the current slow-mode delay of this channel. /// /// - /// An representing the bit-rate set for the voice channel; null if not - /// applicable. + /// An representing the time in seconds required before the user can send another + /// message; 0 if disabled. + /// null if this is not mentioned in this entry. /// - public int? Bitrate { get; } + public int? SlowModeInterval { get; } + /// + /// Gets the value that indicates whether this channel is NSFW. + /// + /// + /// true if this channel has the NSFW flag enabled; otherwise false. + /// null if this is not mentioned in this entry. + /// + public bool? IsNsfw { get; } /// - /// Gets the number of users allowed to be in this channel if applicable. + /// Gets the bit-rate of this channel if applicable. /// /// - /// An representing the number of users allowed to be in this voice channel; - /// null if not applicable. + /// An representing the bit-rate set for the voice channel; + /// null if this is not mentioned in this entry. /// - public int? UserLimit { get; } + public int? Bitrate { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs index f37404906..fa5233145 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/ChannelUpdateAuditLogData.cs @@ -23,20 +23,23 @@ namespace Discord.Rest var nameModel = changes.FirstOrDefault(x => x.ChangedProperty == "name"); var topicModel = changes.FirstOrDefault(x => x.ChangedProperty == "topic"); + var rateLimitPerUserModel = changes.FirstOrDefault(x => x.ChangedProperty == "rate_limit_per_user"); + var nsfwModel = changes.FirstOrDefault(x => x.ChangedProperty == "nsfw"); var bitrateModel = changes.FirstOrDefault(x => x.ChangedProperty == "bitrate"); - var userLimitModel = changes.FirstOrDefault(x => x.ChangedProperty == "user_limit"); string oldName = nameModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newName = nameModel?.NewValue?.ToObject(discord.ApiClient.Serializer); string oldTopic = topicModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newTopic = topicModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + int? oldRateLimitPerUser = rateLimitPerUserModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newRateLimitPerUser = rateLimitPerUserModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + bool? oldNsfw = nsfwModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newNsfw = nsfwModel?.NewValue?.ToObject(discord.ApiClient.Serializer); int? oldBitrate = bitrateModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newBitrate = bitrateModel?.NewValue?.ToObject(discord.ApiClient.Serializer); - int? oldLimit = userLimitModel?.OldValue?.ToObject(discord.ApiClient.Serializer), - newLimit = userLimitModel?.NewValue?.ToObject(discord.ApiClient.Serializer); - var before = new ChannelInfo(oldName, oldTopic, oldBitrate, oldLimit); - var after = new ChannelInfo(newName, newTopic, newBitrate, newLimit); + var before = new ChannelInfo(oldName, oldTopic, oldRateLimitPerUser, oldNsfw, oldBitrate); + var after = new ChannelInfo(newName, newTopic, newRateLimitPerUser, newNsfw, newBitrate); return new ChannelUpdateAuditLogData(entry.TargetId.Value, before, after); } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs index df63251d8..85c7ac438 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildInfo.cs @@ -7,7 +7,8 @@ namespace Discord.Rest { internal GuildInfo(int? afkTimeout, DefaultMessageNotifications? defaultNotifs, ulong? afkChannel, string name, string region, string icon, - VerificationLevel? verification, IUser owner, MfaLevel? mfa, int? filter) + VerificationLevel? verification, IUser owner, MfaLevel? mfa, ExplicitContentFilterLevel? filter, + ulong? systemChannel, ulong? widgetChannel, bool? widget) { AfkTimeout = afkTimeout; DefaultMessageNotifications = defaultNotifs; @@ -18,7 +19,10 @@ namespace Discord.Rest VerificationLevel = verification; Owner = owner; MfaLevel = mfa; - ContentFilterLevel = filter; + ExplicitContentFilter = filter; + SystemChannelId = systemChannel; + EmbedChannelId = widgetChannel; + IsEmbeddable = widget; } /// @@ -28,11 +32,16 @@ namespace Discord.Rest /// /// An representing the amount of time in seconds for a user to be marked as inactive /// and moved into the AFK voice channel. + /// null if this is not mentioned in this entry. /// public int? AfkTimeout { get; } /// /// Gets the default message notifications for users who haven't explicitly set their notification settings. /// + /// + /// The default message notifications setting of this guild. + /// null if this is not mentioned in this entry. + /// public DefaultMessageNotifications? DefaultMessageNotifications { get; } /// /// Gets the ID of the AFK voice channel for this guild. @@ -65,6 +74,7 @@ namespace Discord.Rest /// /// /// The level of requirements. + /// null if this is not mentioned in this entry. /// public VerificationLevel? VerificationLevel { get; } /// @@ -80,8 +90,39 @@ namespace Discord.Rest /// /// /// The level of MFA requirement. + /// null if this is not mentioned in this entry. /// public MfaLevel? MfaLevel { get; } - public int? ContentFilterLevel { get; } + /// + /// Gets the level of content filtering applied to user's content in a Guild. + /// + /// + /// The level of explicit content filtering. + /// + public ExplicitContentFilterLevel? ExplicitContentFilter { get; } + /// + /// Gets the ID of the channel where system messages are sent. + /// + /// + /// A representing the snowflake identifier of the channel where system + /// messages are sent; null if none is set. + /// + public ulong? SystemChannelId { get; } + /// + /// Gets the ID of the widget embed channel of this guild. + /// + /// + /// A representing the snowflake identifier of the embedded channel found within the + /// widget settings of this guild; null if none is set. + /// + public ulong? EmbedChannelId { get; } + /// + /// Gets a value that indicates whether this guild is embeddable (i.e. can use widget). + /// + /// + /// true if this guild can be embedded via widgets; otherwise false. + /// null if this is not mentioned in this entry. + /// + public bool? IsEmbeddable { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs index 853fa4b4a..80b719a6d 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/GuildUpdateAuditLogData.cs @@ -30,6 +30,9 @@ namespace Discord.Rest var ownerIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "owner_id"); var mfaLevelModel = changes.FirstOrDefault(x => x.ChangedProperty == "mfa_level"); var contentFilterModel = changes.FirstOrDefault(x => x.ChangedProperty == "explicit_content_filter"); + var systemChannelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "system_channel_id"); + var widgetChannelIdModel = changes.FirstOrDefault(x => x.ChangedProperty == "widget_channel_id"); + var widgetEnabledModel = changes.FirstOrDefault(x => x.ChangedProperty == "widget_enabled"); int? oldAfkTimeout = afkTimeoutModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newAfkTimeout = afkTimeoutModel?.NewValue?.ToObject(discord.ApiClient.Serializer); @@ -49,8 +52,14 @@ namespace Discord.Rest newOwnerId = ownerIdModel?.NewValue?.ToObject(discord.ApiClient.Serializer); MfaLevel? oldMfaLevel = mfaLevelModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newMfaLevel = mfaLevelModel?.NewValue?.ToObject(discord.ApiClient.Serializer); - int? oldContentFilter = contentFilterModel?.OldValue?.ToObject(discord.ApiClient.Serializer), - newContentFilter = contentFilterModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + ExplicitContentFilterLevel? oldContentFilter = contentFilterModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newContentFilter = contentFilterModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + ulong? oldSystemChannelId = systemChannelIdModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newSystemChannelId = systemChannelIdModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + ulong? oldWidgetChannelId = widgetChannelIdModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newWidgetChannelId = widgetChannelIdModel?.NewValue?.ToObject(discord.ApiClient.Serializer); + bool? oldWidgetEnabled = widgetEnabledModel?.OldValue?.ToObject(discord.ApiClient.Serializer), + newWidgetEnabled = widgetEnabledModel?.NewValue?.ToObject(discord.ApiClient.Serializer); IUser oldOwner = null; if (oldOwnerId != null) @@ -68,10 +77,10 @@ namespace Discord.Rest var before = new GuildInfo(oldAfkTimeout, oldDefaultMessageNotifications, oldAfkChannelId, oldName, oldRegionId, oldIconHash, oldVerificationLevel, oldOwner, - oldMfaLevel, oldContentFilter); + oldMfaLevel, oldContentFilter, oldSystemChannelId, oldWidgetChannelId, oldWidgetEnabled); var after = new GuildInfo(newAfkTimeout, newDefaultMessageNotifications, newAfkChannelId, newName, newRegionId, newIconHash, newVerificationLevel, newOwner, - newMfaLevel, newContentFilter); + newMfaLevel, newContentFilter, newSystemChannelId, newWidgetChannelId, newWidgetEnabled); return new GuildUpdateAuditLogData(before, after); } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberInfo.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberInfo.cs index b0a1a8e5a..ffa316faa 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberInfo.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberInfo.cs @@ -1,18 +1,41 @@ namespace Discord.Rest { + /// + /// Represents information for a member. + /// public struct MemberInfo { - internal MemberInfo(string nick, bool? deaf, bool? mute, string avatar_hash) + internal MemberInfo(string nick, bool? deaf, bool? mute) { Nickname = nick; Deaf = deaf; Mute = mute; - AvatarHash = avatar_hash; } + /// + /// Gets the nickname of the updated member. + /// + /// + /// A string representing the nickname of the updated member; null if none is set. + /// public string Nickname { get; } + /// + /// Gets a value that indicates whether the updated member is deafened by the guild. + /// + /// + /// true if the updated member is deafened (i.e. not permitted to listen to or speak to others) by the guild; + /// otherwise false. + /// null if this is not mentioned in this entry. + /// public bool? Deaf { get; } + /// + /// Gets a value that indicates whether the updated member is muted (i.e. not permitted to speak via voice) by the + /// guild. + /// + /// + /// true if the updated member is muted by the guild; otherwise false. + /// null if this is not mentioned in this entry. + /// public bool? Mute { get; } - public string AvatarHash { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs index 238c79843..f22b83e4c 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/MemberUpdateAuditLogData.cs @@ -24,7 +24,6 @@ namespace Discord.Rest var nickModel = changes.FirstOrDefault(x => x.ChangedProperty == "nick"); var deafModel = changes.FirstOrDefault(x => x.ChangedProperty == "deaf"); var muteModel = changes.FirstOrDefault(x => x.ChangedProperty == "mute"); - var avatarModel = changes.FirstOrDefault(x => x.ChangedProperty == "avatar_hash"); string oldNick = nickModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newNick = nickModel?.NewValue?.ToObject(discord.ApiClient.Serializer); @@ -32,14 +31,12 @@ namespace Discord.Rest newDeaf = deafModel?.NewValue?.ToObject(discord.ApiClient.Serializer); bool? oldMute = muteModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newMute = muteModel?.NewValue?.ToObject(discord.ApiClient.Serializer); - string oldAvatar = avatarModel?.OldValue?.ToObject(discord.ApiClient.Serializer), - newAvatar = avatarModel?.NewValue?.ToObject(discord.ApiClient.Serializer); var targetInfo = log.Users.FirstOrDefault(x => x.Id == entry.TargetId); var user = RestUser.Create(discord, targetInfo); - var before = new MemberInfo(oldNick, oldDeaf, oldMute, oldAvatar); - var after = new MemberInfo(newNick, newDeaf, newMute, newAvatar); + var before = new MemberInfo(oldNick, oldDeaf, oldMute); + var after = new MemberInfo(newNick, newDeaf, newMute); return new MemberUpdateAuditLogData(user, before, after); } @@ -51,7 +48,19 @@ namespace Discord.Rest /// A user object representing the user who the changes were performed on. /// public IUser Target { get; } + /// + /// Gets the member information before the changes. + /// + /// + /// An information object containing the original member information before the changes were made. + /// public MemberInfo Before { get; } + /// + /// Gets the member information after the changes. + /// + /// + /// An information object containing the member information after the changes were made. + /// public MemberInfo After { get; } } } diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs index a9ccd4db3..3f391187d 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteCreateAuditLogData.cs @@ -10,8 +10,9 @@ namespace Discord.Rest /// public class OverwriteCreateAuditLogData : IAuditLogData { - private OverwriteCreateAuditLogData(Overwrite overwrite) + private OverwriteCreateAuditLogData(ulong channelId, Overwrite overwrite) { + ChannelId = channelId; Overwrite = overwrite; } @@ -30,10 +31,18 @@ namespace Discord.Rest var id = entry.Options.OverwriteTargetId.Value; var type = entry.Options.OverwriteType; - return new OverwriteCreateAuditLogData(new Overwrite(id, type, permissions)); + return new OverwriteCreateAuditLogData(entry.TargetId.Value, new Overwrite(id, type, permissions)); } /// + /// Gets the ID of the channel that the overwrite was created from. + /// + /// + /// A representing the snowflake identifier for the channel that the overwrite was + /// created from. + /// + public ulong ChannelId { get; } + /// /// Gets the permission overwrite object that was created. /// /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs index 1e51fa5e3..a193e76ce 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteDeleteAuditLogData.cs @@ -10,8 +10,9 @@ namespace Discord.Rest /// public class OverwriteDeleteAuditLogData : IAuditLogData { - private OverwriteDeleteAuditLogData(Overwrite deletedOverwrite) + private OverwriteDeleteAuditLogData(ulong channelId, Overwrite deletedOverwrite) { + ChannelId = channelId; Overwrite = deletedOverwrite; } @@ -29,10 +30,18 @@ namespace Discord.Rest var id = idModel.OldValue.ToObject(discord.ApiClient.Serializer); var allow = allowModel.OldValue.ToObject(discord.ApiClient.Serializer); - return new OverwriteDeleteAuditLogData(new Overwrite(id, type, new OverwritePermissions(allow, deny))); + return new OverwriteDeleteAuditLogData(entry.TargetId.Value, new Overwrite(id, type, new OverwritePermissions(allow, deny))); } /// + /// Gets the ID of the channel that the overwrite was deleted from. + /// + /// + /// A representing the snowflake identifier for the channel that the overwrite was + /// deleted from. + /// + public ulong ChannelId { get; } + /// /// Gets the permission overwrite object that was deleted. /// /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs index ac67c85cf..c2b8d423e 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/OverwriteUpdateAuditLogData.cs @@ -10,8 +10,9 @@ namespace Discord.Rest /// public class OverwriteUpdateAuditLogData : IAuditLogData { - private OverwriteUpdateAuditLogData(OverwritePermissions before, OverwritePermissions after, ulong targetId, PermissionTarget targetType) + private OverwriteUpdateAuditLogData(ulong channelId, OverwritePermissions before, OverwritePermissions after, ulong targetId, PermissionTarget targetType) { + ChannelId = channelId; OldPermissions = before; NewPermissions = after; OverwriteTargetId = targetId; @@ -28,17 +29,25 @@ namespace Discord.Rest var beforeAllow = allowModel?.OldValue?.ToObject(discord.ApiClient.Serializer); var afterAllow = allowModel?.NewValue?.ToObject(discord.ApiClient.Serializer); var beforeDeny = denyModel?.OldValue?.ToObject(discord.ApiClient.Serializer); - var afterDeny = denyModel?.OldValue?.ToObject(discord.ApiClient.Serializer); + var afterDeny = denyModel?.NewValue?.ToObject(discord.ApiClient.Serializer); var beforePermissions = new OverwritePermissions(beforeAllow ?? 0, beforeDeny ?? 0); var afterPermissions = new OverwritePermissions(afterAllow ?? 0, afterDeny ?? 0); var type = entry.Options.OverwriteType; - return new OverwriteUpdateAuditLogData(beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, type); + return new OverwriteUpdateAuditLogData(entry.TargetId.Value, beforePermissions, afterPermissions, entry.Options.OverwriteTargetId.Value, type); } /// + /// Gets the ID of the channel that the overwrite was updated from. + /// + /// + /// A representing the snowflake identifier for the channel that the overwrite was + /// updated from. + /// + public ulong ChannelId { get; } + /// /// Gets the overwrite permissions before the changes. /// /// diff --git a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs index f98fc3e23..094e1e0e0 100644 --- a/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs +++ b/src/Discord.Net.Rest/Entities/AuditLogs/DataTypes/RoleUpdateAuditLogData.cs @@ -36,7 +36,7 @@ namespace Discord.Rest string oldName = nameModel?.OldValue?.ToObject(discord.ApiClient.Serializer), newName = nameModel?.NewValue?.ToObject(discord.ApiClient.Serializer); ulong? oldPermissionsRaw = permissionsModel?.OldValue?.ToObject(discord.ApiClient.Serializer), - newPermissionsRaw = permissionsModel?.OldValue?.ToObject(discord.ApiClient.Serializer); + newPermissionsRaw = permissionsModel?.NewValue?.ToObject(discord.ApiClient.Serializer); Color? oldColor = null, newColor = null; diff --git a/src/Discord.Net.Rest/Entities/Users/RestUser.cs b/src/Discord.Net.Rest/Entities/Users/RestUser.cs index 37385fb7e..d5fffca94 100644 --- a/src/Discord.Net.Rest/Entities/Users/RestUser.cs +++ b/src/Discord.Net.Rest/Entities/Users/RestUser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Globalization; using System.Threading.Tasks; using Model = Discord.API.User; @@ -57,7 +58,7 @@ namespace Discord.Rest if (model.Avatar.IsSpecified) AvatarId = model.Avatar.Value; if (model.Discriminator.IsSpecified) - DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + DiscriminatorValue = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); if (model.Bot.IsSpecified) IsBot = model.Bot.Value; if (model.Username.IsSpecified) diff --git a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs b/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs index ae8cf2cb2..e55534833 100644 --- a/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs +++ b/src/Discord.Net.Rest/Net/Converters/UInt64EntityOrIdConverter.cs @@ -1,6 +1,7 @@ -using Discord.API; +using Discord.API; using Newtonsoft.Json; using System; +using System.Globalization; namespace Discord.Net.Converters { @@ -23,7 +24,7 @@ namespace Discord.Net.Converters { case JsonToken.String: case JsonToken.Integer: - return new EntityOrId(ulong.Parse(reader.ReadAsString())); + return new EntityOrId(ulong.Parse(reader.ReadAsString(), NumberStyles.None, CultureInfo.InvariantCulture)); default: T obj; if (_innerConverter != null) diff --git a/src/Discord.Net.Rest/Net/RateLimitInfo.cs b/src/Discord.Net.Rest/Net/RateLimitInfo.cs index 5722b50f2..13e9e39a7 100644 --- a/src/Discord.Net.Rest/Net/RateLimitInfo.cs +++ b/src/Discord.Net.Rest/Net/RateLimitInfo.cs @@ -11,7 +11,7 @@ namespace Discord.Net public int? Remaining { get; } public int? RetryAfter { get; } public DateTimeOffset? Reset { get; } - public TimeSpan? ResetAfter { get; } + public TimeSpan? ResetAfter { get; } public TimeSpan? Lag { get; } internal RateLimitInfo(Dictionary headers) @@ -19,17 +19,17 @@ namespace Discord.Net IsGlobal = headers.TryGetValue("X-RateLimit-Global", out string temp) && bool.TryParse(temp, out var isGlobal) && isGlobal; Limit = headers.TryGetValue("X-RateLimit-Limit", out temp) && - int.TryParse(temp, out var limit) ? limit : (int?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var limit) ? limit : (int?)null; Remaining = headers.TryGetValue("X-RateLimit-Remaining", out temp) && - int.TryParse(temp, out var remaining) ? remaining : (int?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var remaining) ? remaining : (int?)null; Reset = headers.TryGetValue("X-RateLimit-Reset", out temp) && double.TryParse(temp, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var reset) ? DateTimeOffset.FromUnixTimeMilliseconds((long)(reset * 1000)) : (DateTimeOffset?)null; RetryAfter = headers.TryGetValue("Retry-After", out temp) && - int.TryParse(temp, out var retryAfter) ? retryAfter : (int?)null; - ResetAfter = headers.TryGetValue("X-RateLimit-Reset-After", out temp) && - float.TryParse(temp, out var resetAfter) ? TimeSpan.FromMilliseconds((long)(resetAfter * 1000)) : (TimeSpan?)null; + int.TryParse(temp, NumberStyles.None, CultureInfo.InvariantCulture, out var retryAfter) ? retryAfter : (int?)null; + ResetAfter = headers.TryGetValue("X-RateLimit-Reset-After", out temp) && + float.TryParse(temp, out var resetAfter) ? TimeSpan.FromMilliseconds((long)(resetAfter * 1000)) : (TimeSpan?)null; Lag = headers.TryGetValue("Date", out temp) && - DateTimeOffset.TryParse(temp, out var date) ? DateTimeOffset.UtcNow - date : (TimeSpan?)null; + DateTimeOffset.TryParse(temp, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date) ? DateTimeOffset.UtcNow - date : (TimeSpan?)null; } } } diff --git a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs index eceb071eb..d4798bedd 100644 --- a/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs +++ b/src/Discord.Net.WebSocket/Entities/Users/SocketUser.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Threading.Tasks; using Discord.Rest; @@ -60,10 +61,10 @@ namespace Discord.WebSocket } if (model.Discriminator.IsSpecified) { - var newVal = ushort.Parse(model.Discriminator.Value); + var newVal = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); if (newVal != DiscriminatorValue) { - DiscriminatorValue = ushort.Parse(model.Discriminator.Value); + DiscriminatorValue = ushort.Parse(model.Discriminator.Value, NumberStyles.None, CultureInfo.InvariantCulture); hasChanges = true; } } diff --git a/src/Discord.Net.Webhook/DiscordWebhookClient.cs b/src/Discord.Net.Webhook/DiscordWebhookClient.cs index 5dc3d51aa..542ec7997 100644 --- a/src/Discord.Net.Webhook/DiscordWebhookClient.cs +++ b/src/Discord.Net.Webhook/DiscordWebhookClient.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -132,7 +133,7 @@ namespace Discord.Webhook { // ensure that the first group is a ulong, set the _webhookId // 0th group is always the entire match, so start at index 1 - if (!(match.Groups[1].Success && ulong.TryParse(match.Groups[1].Value, out webhookId))) + if (!(match.Groups[1].Success && ulong.TryParse(match.Groups[1].Value, NumberStyles.None, CultureInfo.InvariantCulture, out webhookId))) throw ex("The webhook Id could not be parsed."); if (!match.Groups[2].Success) diff --git a/test/Discord.Net.Tests.Unit/ChannelPermissionsTests.cs b/test/Discord.Net.Tests.Unit/ChannelPermissionsTests.cs index 2625c1e9b..a3566590a 100644 --- a/test/Discord.Net.Tests.Unit/ChannelPermissionsTests.cs +++ b/test/Discord.Net.Tests.Unit/ChannelPermissionsTests.cs @@ -82,6 +82,7 @@ namespace Discord AssertFlag(() => new ChannelPermissions(moveMembers: true), ChannelPermission.MoveMembers); AssertFlag(() => new ChannelPermissions(useVoiceActivation: true), ChannelPermission.UseVAD); AssertFlag(() => new ChannelPermissions(prioritySpeaker: true), ChannelPermission.PrioritySpeaker); + AssertFlag(() => new ChannelPermissions(stream: true), ChannelPermission.Stream); AssertFlag(() => new ChannelPermissions(manageRoles: true), ChannelPermission.ManageRoles); AssertFlag(() => new ChannelPermissions(manageWebhooks: true), ChannelPermission.ManageWebhooks); } @@ -147,6 +148,7 @@ namespace Discord AssertUtil(ChannelPermission.ManageRoles, x => x.ManageRoles, (p, enable) => p.Modify(manageRoles: enable)); AssertUtil(ChannelPermission.ManageWebhooks, x => x.ManageWebhooks, (p, enable) => p.Modify(manageWebhooks: enable)); AssertUtil(ChannelPermission.PrioritySpeaker, x => x.PrioritySpeaker, (p, enable) => p.Modify(prioritySpeaker: enable)); + AssertUtil(ChannelPermission.Stream, x => x.Stream, (p, enable) => p.Modify(stream: enable)); } /// diff --git a/test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs b/test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs index 8213a8974..f0611fa24 100644 --- a/test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs +++ b/test/Discord.Net.Tests.Unit/GuildPermissionsTests.cs @@ -85,6 +85,7 @@ namespace Discord AssertFlag(() => new GuildPermissions(moveMembers: true), GuildPermission.MoveMembers); AssertFlag(() => new GuildPermissions(useVoiceActivation: true), GuildPermission.UseVAD); AssertFlag(() => new GuildPermissions(prioritySpeaker: true), GuildPermission.PrioritySpeaker); + AssertFlag(() => new GuildPermissions(stream: true), GuildPermission.Stream); AssertFlag(() => new GuildPermissions(changeNickname: true), GuildPermission.ChangeNickname); AssertFlag(() => new GuildPermissions(manageNicknames: true), GuildPermission.ManageNicknames); AssertFlag(() => new GuildPermissions(manageRoles: true), GuildPermission.ManageRoles);