| @@ -164,8 +164,7 @@ namespace Discord.Commands | |||||
| try | try | ||||
| { | { | ||||
| ModuleInfo module; | ModuleInfo module; | ||||
| _typedModuleDefs.TryGetValue(type, out module); | |||||
| if (module == default(ModuleInfo)) | |||||
| if (!_typedModuleDefs.TryRemove(type, out module)) | |||||
| return false; | return false; | ||||
| return RemoveModuleInternal(module); | return RemoveModuleInternal(module); | ||||
| @@ -2,18 +2,19 @@ | |||||
| namespace Discord | namespace Discord | ||||
| { | { | ||||
| //Source: https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs | |||||
| internal static class DateTimeUtils | internal static class DateTimeUtils | ||||
| { | { | ||||
| #if !NETSTANDARD1_3 | #if !NETSTANDARD1_3 | ||||
| //https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/DateTimeOffset.cs | |||||
| private const long UnixEpochTicks = 621355968000000000; | |||||
| private const long UnixEpochSeconds = 62135596800; | |||||
| private const long UnixEpochTicks = 621_355_968_000_000_000; | |||||
| private const long UnixEpochSeconds = 62_135_596_800; | |||||
| private const long UnixEpochMilliseconds = 62_135_596_800_000; | |||||
| #endif | #endif | ||||
| public static DateTimeOffset FromSnowflake(ulong value) | public static DateTimeOffset FromSnowflake(ulong value) | ||||
| => FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); | => FromUnixMilliseconds((long)((value >> 22) + 1420070400000UL)); | ||||
| public static ulong ToSnowflake(DateTimeOffset value) | public static ulong ToSnowflake(DateTimeOffset value) | ||||
| => (ulong)(ToUnixMilliseconds(value) - 1420070400000L) << 22; | |||||
| => ((ulong)ToUnixMilliseconds(value) - 1420070400000UL) << 22; | |||||
| public static DateTimeOffset FromTicks(long ticks) | public static DateTimeOffset FromTicks(long ticks) | ||||
| => new DateTimeOffset(ticks, TimeSpan.Zero); | => new DateTimeOffset(ticks, TimeSpan.Zero); | ||||
| @@ -29,12 +30,12 @@ namespace Discord | |||||
| return new DateTimeOffset(ticks, TimeSpan.Zero); | return new DateTimeOffset(ticks, TimeSpan.Zero); | ||||
| #endif | #endif | ||||
| } | } | ||||
| public static DateTimeOffset FromUnixMilliseconds(long seconds) | |||||
| public static DateTimeOffset FromUnixMilliseconds(long milliseconds) | |||||
| { | { | ||||
| #if NETSTANDARD1_3 | #if NETSTANDARD1_3 | ||||
| return DateTimeOffset.FromUnixTimeMilliseconds(seconds); | |||||
| return DateTimeOffset.FromUnixTimeMilliseconds(milliseconds); | |||||
| #else | #else | ||||
| long ticks = seconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; | |||||
| long ticks = milliseconds * TimeSpan.TicksPerMillisecond + UnixEpochTicks; | |||||
| return new DateTimeOffset(ticks, TimeSpan.Zero); | return new DateTimeOffset(ticks, TimeSpan.Zero); | ||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -53,8 +54,8 @@ namespace Discord | |||||
| #if NETSTANDARD1_3 | #if NETSTANDARD1_3 | ||||
| return dto.ToUnixTimeMilliseconds(); | return dto.ToUnixTimeMilliseconds(); | ||||
| #else | #else | ||||
| long seconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; | |||||
| return seconds - UnixEpochSeconds; | |||||
| long milliseconds = dto.UtcDateTime.Ticks / TimeSpan.TicksPerMillisecond; | |||||
| return milliseconds - UnixEpochMilliseconds; | |||||
| #endif | #endif | ||||
| } | } | ||||
| } | } | ||||
| @@ -185,9 +185,12 @@ namespace Discord | |||||
| // Bulk Delete | // Bulk Delete | ||||
| public static void YoungerThanTwoWeeks(ulong[] collection, string name) | public static void YoungerThanTwoWeeks(ulong[] collection, string name) | ||||
| { | { | ||||
| var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.Now.Subtract(TimeSpan.FromMilliseconds(1209540000))); | |||||
| var minimum = DateTimeUtils.ToSnowflake(DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(14))); | |||||
| for (var i = 0; i < collection.Length; i++) | for (var i = 0; i < collection.Length; i++) | ||||
| if (collection[i] <= minimum) throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks to delete."); | |||||
| { | |||||
| if (collection[i] <= minimum) | |||||
| throw new ArgumentOutOfRangeException(name, "Messages must be younger than two weeks old."); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -494,13 +494,8 @@ namespace Discord.API | |||||
| if (args.Content.GetValueOrDefault(null) == null) | if (args.Content.GetValueOrDefault(null) == null) | ||||
| args.Content = ""; | args.Content = ""; | ||||
| else if (args.Content.IsSpecified) | |||||
| { | |||||
| if (args.Content.Value == null) | |||||
| args.Content = ""; | |||||
| if (args.Content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| } | |||||
| else if (args.Content.IsSpecified && args.Content.Value?.Length > DiscordConfig.MaxMessageSize) | |||||
| throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content)); | |||||
| var ids = new BucketIds(channelId: channelId); | var ids = new BucketIds(channelId: channelId); | ||||
| return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | return await SendMultipartAsync<Message>("POST", () => $"channels/{channelId}/messages", args.ToDictionary(), ids, clientBucket: ClientBucketType.SendEdit, options: options).ConfigureAwait(false); | ||||
| @@ -101,7 +101,14 @@ namespace Discord.Net.Rest | |||||
| if (p.Value is MultipartFile) | if (p.Value is MultipartFile) | ||||
| { | { | ||||
| var fileValue = (MultipartFile)p.Value; | var fileValue = (MultipartFile)p.Value; | ||||
| content.Add(new StreamContent(fileValue.Stream), p.Key, fileValue.Filename); | |||||
| var stream = fileValue.Stream; | |||||
| if (!stream.CanSeek) | |||||
| { | |||||
| var memoryStream = new MemoryStream(); | |||||
| await stream.CopyToAsync(memoryStream).ConfigureAwait(false); | |||||
| stream = memoryStream; | |||||
| } | |||||
| content.Add(new StreamContent(stream), p.Key, fileValue.Filename); | |||||
| continue; | continue; | ||||
| } | } | ||||
| @@ -7,8 +7,8 @@ namespace Discord.Rpc | |||||
| public class RpcChannelSummary | public class RpcChannelSummary | ||||
| { | { | ||||
| public ulong Id { get; } | public ulong Id { get; } | ||||
| public string Name { get; set; } | |||||
| public ChannelType Type { get; set; } | |||||
| public string Name { get; private set; } | |||||
| public ChannelType Type { get; private set; } | |||||
| internal RpcChannelSummary(ulong id) | internal RpcChannelSummary(ulong id) | ||||
| { | { | ||||
| @@ -29,6 +29,14 @@ namespace Discord.WebSocket | |||||
| internal override void Update(ClientState state, PresenceModel model) | internal override void Update(ClientState state, PresenceModel model) | ||||
| { | { | ||||
| if (model.User.Avatar.IsSpecified) | |||||
| AvatarId = model.User.Avatar.Value; | |||||
| if (model.User.Discriminator.IsSpecified) | |||||
| DiscriminatorValue = ushort.Parse(model.User.Discriminator.Value); | |||||
| if (model.User.Bot.IsSpecified) | |||||
| IsBot = model.User.Bot.Value; | |||||
| if (model.User.Username.IsSpecified) | |||||
| Username = model.User.Username.Value; | |||||
| } | } | ||||
| internal new SocketSimpleUser Clone() => MemberwiseClone() as SocketSimpleUser; | internal new SocketSimpleUser Clone() => MemberwiseClone() as SocketSimpleUser; | ||||