| @@ -45,11 +45,13 @@ namespace Discord.API | |||||
| } | } | ||||
| _restClient = restClientProvider(DiscordConfig.ClientAPIUrl, cancelToken); | _restClient = restClientProvider(DiscordConfig.ClientAPIUrl, cancelToken); | ||||
| _restClient.SetHeader("accept", "*/*"); | |||||
| _restClient.SetHeader("authorization", authToken); | _restClient.SetHeader("authorization", authToken); | ||||
| _restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | _restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | ||||
| _requestQueue = new RequestQueue(_restClient); | _requestQueue = new RequestQueue(_restClient); | ||||
| _serializer = new JsonSerializer(); | _serializer = new JsonSerializer(); | ||||
| _serializer.Converters.Add(new OptionalConverter()); | |||||
| _serializer.Converters.Add(new ChannelTypeConverter()); | _serializer.Converters.Add(new ChannelTypeConverter()); | ||||
| _serializer.Converters.Add(new ImageConverter()); | _serializer.Converters.Add(new ImageConverter()); | ||||
| _serializer.Converters.Add(new NullableUInt64Converter()); | _serializer.Converters.Add(new NullableUInt64Converter()); | ||||
| @@ -59,58 +61,59 @@ namespace Discord.API | |||||
| _serializer.Converters.Add(new UInt64Converter()); | _serializer.Converters.Add(new UInt64Converter()); | ||||
| _serializer.Converters.Add(new UInt64EntityConverter()); | _serializer.Converters.Add(new UInt64EntityConverter()); | ||||
| _serializer.Converters.Add(new UserStatusConverter()); | _serializer.Converters.Add(new UserStatusConverter()); | ||||
| _serializer.ContractResolver = new OptionalContractResolver(); | |||||
| } | } | ||||
| //Core | //Core | ||||
| public Task Send(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General) | public Task Send(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General) | ||||
| => SendInternal(method, endpoint, null, bucket); | |||||
| => SendInternal(method, endpoint, null, true, bucket); | |||||
| public Task Send(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General) | public Task Send(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General) | ||||
| => SendInternal(method, endpoint, payload, bucket); | |||||
| => SendInternal(method, endpoint, payload, true, bucket); | |||||
| public Task Send(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General) | public Task Send(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General) | ||||
| => SendInternal(method, endpoint, multipartArgs, bucket); | |||||
| => SendInternal(method, endpoint, multipartArgs, true, bucket); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, null, bucket).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, null, false, bucket).ConfigureAwait(false)); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket = GlobalBucket.General) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, payload, bucket).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, payload, false, bucket).ConfigureAwait(false)); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GlobalBucket bucket = GlobalBucket.General) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, bucket).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket).ConfigureAwait(false)); | |||||
| public Task Send(string method, string endpoint, GuildBucket bucket, ulong guildId) | public Task Send(string method, string endpoint, GuildBucket bucket, ulong guildId) | ||||
| => SendInternal(method, endpoint, null, bucket, guildId); | |||||
| => SendInternal(method, endpoint, null, true, bucket, guildId); | |||||
| public Task Send(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId) | public Task Send(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId) | ||||
| => SendInternal(method, endpoint, payload, bucket, guildId); | |||||
| => SendInternal(method, endpoint, payload, true, bucket, guildId); | |||||
| public Task Send(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId) | public Task Send(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId) | ||||
| => SendInternal(method, endpoint, multipartArgs, bucket, guildId); | |||||
| => SendInternal(method, endpoint, multipartArgs, true, bucket, guildId); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, GuildBucket bucket, ulong guildId) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, GuildBucket bucket, ulong guildId) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, null, bucket, guildId).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, null, false, bucket, guildId).ConfigureAwait(false)); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, payload, bucket, guildId).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, payload, false, bucket, guildId).ConfigureAwait(false)); | |||||
| public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId) | public async Task<TResponse> Send<TResponse>(string method, string endpoint, Stream file, IReadOnlyDictionary<string, string> multipartArgs, GuildBucket bucket, ulong guildId) | ||||
| where TResponse : class | where TResponse : class | ||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, bucket, guildId).ConfigureAwait(false)); | |||||
| => Deserialize<TResponse>(await SendInternal(method, endpoint, multipartArgs, false, bucket, guildId).ConfigureAwait(false)); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, object payload, GlobalBucket bucket) | |||||
| => SendInternal(method, endpoint, payload, BucketGroup.Global, (int)bucket, 0); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, object payload, GuildBucket bucket, ulong guildId) | |||||
| => SendInternal(method, endpoint, payload, BucketGroup.Guild, (int)bucket, guildId); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, GlobalBucket bucket) | |||||
| => SendInternal(method, endpoint, multipartArgs, BucketGroup.Global, (int)bucket, 0); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, GuildBucket bucket, ulong guildId) | |||||
| => SendInternal(method, endpoint, multipartArgs, BucketGroup.Guild, (int)bucket, guildId); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, object payload, bool headerOnly, GlobalBucket bucket) | |||||
| => SendInternal(method, endpoint, payload, headerOnly, BucketGroup.Global, (int)bucket, 0); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, object payload, bool headerOnly, GuildBucket bucket, ulong guildId) | |||||
| => SendInternal(method, endpoint, payload, headerOnly, BucketGroup.Guild, (int)bucket, guildId); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly, GlobalBucket bucket) | |||||
| => SendInternal(method, endpoint, multipartArgs, headerOnly, BucketGroup.Global, (int)bucket, 0); | |||||
| private Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly, GuildBucket bucket, ulong guildId) | |||||
| => SendInternal(method, endpoint, multipartArgs, headerOnly, BucketGroup.Guild, (int)bucket, guildId); | |||||
| private async Task<Stream> SendInternal(string method, string endpoint, object payload, BucketGroup group, int bucketId, ulong guildId) | |||||
| private async Task<Stream> SendInternal(string method, string endpoint, object payload, bool headerOnly, BucketGroup group, int bucketId, ulong guildId) | |||||
| { | { | ||||
| var stopwatch = Stopwatch.StartNew(); | var stopwatch = Stopwatch.StartNew(); | ||||
| string json = null; | string json = null; | ||||
| if (payload != null) | if (payload != null) | ||||
| json = Serialize(payload); | json = Serialize(payload); | ||||
| var responseStream = await _requestQueue.Send(new RestRequest(method, endpoint, json), group, bucketId, guildId).ConfigureAwait(false); | |||||
| int bytes = (int)responseStream.Length; | |||||
| var responseStream = await _requestQueue.Send(new RestRequest(method, endpoint, json, headerOnly), group, bucketId, guildId).ConfigureAwait(false); | |||||
| int bytes = headerOnly ? 0 : (int)responseStream.Length; | |||||
| stopwatch.Stop(); | stopwatch.Stop(); | ||||
| double milliseconds = ToMilliseconds(stopwatch); | double milliseconds = ToMilliseconds(stopwatch); | ||||
| @@ -118,11 +121,11 @@ namespace Discord.API | |||||
| return responseStream; | return responseStream; | ||||
| } | } | ||||
| private async Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, BucketGroup group, int bucketId, ulong guildId) | |||||
| private async Task<Stream> SendInternal(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly, BucketGroup group, int bucketId, ulong guildId) | |||||
| { | { | ||||
| var stopwatch = Stopwatch.StartNew(); | var stopwatch = Stopwatch.StartNew(); | ||||
| var responseStream = await _requestQueue.Send(new RestRequest(method, endpoint, multipartArgs), group, bucketId, guildId).ConfigureAwait(false); | |||||
| int bytes = (int)responseStream.Length; | |||||
| var responseStream = await _requestQueue.Send(new RestRequest(method, endpoint, multipartArgs, headerOnly), group, bucketId, guildId).ConfigureAwait(false); | |||||
| int bytes = headerOnly ? 0 : (int)responseStream.Length; | |||||
| stopwatch.Stop(); | stopwatch.Stop(); | ||||
| double milliseconds = ToMilliseconds(stopwatch); | double milliseconds = ToMilliseconds(stopwatch); | ||||
| @@ -448,11 +451,11 @@ namespace Discord.API | |||||
| if (args.Limit <= 0) throw new ArgumentOutOfRangeException(nameof(args.Limit)); | if (args.Limit <= 0) throw new ArgumentOutOfRangeException(nameof(args.Limit)); | ||||
| if (args.Offset < 0) throw new ArgumentOutOfRangeException(nameof(args.Offset)); | if (args.Offset < 0) throw new ArgumentOutOfRangeException(nameof(args.Offset)); | ||||
| int limit = args.Limit ?? int.MaxValue; | |||||
| int limit = args.Limit.IsSpecified ? args.Limit.Value : int.MaxValue; | |||||
| int offset = args.Offset; | int offset = args.Offset; | ||||
| List<GuildMember[]> result; | List<GuildMember[]> result; | ||||
| if (args.Limit != null) | |||||
| if (args.Limit.IsSpecified) | |||||
| result = new List<GuildMember[]>((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch); | result = new List<GuildMember[]>((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch); | ||||
| else | else | ||||
| result = new List<GuildMember[]>(); | result = new List<GuildMember[]>(); | ||||
| @@ -488,13 +491,13 @@ namespace Discord.API | |||||
| await Send("DELETE", $"guilds/{guildId}/members/{userId}").ConfigureAwait(false); | await Send("DELETE", $"guilds/{guildId}/members/{userId}").ConfigureAwait(false); | ||||
| } | } | ||||
| public async Task<GuildMember> ModifyGuildMember(ulong guildId, ulong userId, ModifyGuildMemberParams args) | |||||
| public async Task ModifyGuildMember(ulong guildId, ulong userId, ModifyGuildMemberParams args) | |||||
| { | { | ||||
| if (args == null) throw new ArgumentNullException(nameof(args)); | if (args == null) throw new ArgumentNullException(nameof(args)); | ||||
| if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | if (guildId == 0) throw new ArgumentOutOfRangeException(nameof(guildId)); | ||||
| if (userId == 0) throw new ArgumentOutOfRangeException(nameof(userId)); | if (userId == 0) throw new ArgumentOutOfRangeException(nameof(userId)); | ||||
| return await Send<GuildMember>("PATCH", $"guilds/{guildId}/members/{userId}", args).ConfigureAwait(false); | |||||
| await Send("PATCH", $"guilds/{guildId}/members/{userId}", args).ConfigureAwait(false); | |||||
| } | } | ||||
| //Guild Roles | //Guild Roles | ||||
| @@ -0,0 +1,8 @@ | |||||
| namespace Discord.API | |||||
| { | |||||
| public interface IOptional | |||||
| { | |||||
| object Value { get; } | |||||
| bool IsSpecified { get; } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,26 @@ | |||||
| namespace Discord.API | |||||
| { | |||||
| public struct Optional<T> : IOptional | |||||
| { | |||||
| /// <summary> Gets the value for this paramter, or default(T) if unspecified. </summary> | |||||
| public T Value { get; } | |||||
| /// <summary> Returns true if this value has been specified. </summary> | |||||
| public bool IsSpecified { get; } | |||||
| object IOptional.Value => Value; | |||||
| /// <summary> Creates a new Parameter with the provided value. </summary> | |||||
| public Optional(T value) | |||||
| { | |||||
| Value = value; | |||||
| IsSpecified = true; | |||||
| } | |||||
| /// <summary> Implicitly creates a new Parameter from an existing value. </summary> | |||||
| public static implicit operator Optional<T>(T value) => new Optional<T>(value); | |||||
| /// <summary> Implicitly creates a new Parameter from an existing value. </summary> | |||||
| public static implicit operator T(Optional<T> param) => param.Value; | |||||
| public override string ToString() => IsSpecified ? (Value?.ToString() ?? null) : null; | |||||
| } | |||||
| } | |||||
| @@ -5,12 +5,12 @@ namespace Discord.API.Rest | |||||
| public class CreateChannelInviteParams | public class CreateChannelInviteParams | ||||
| { | { | ||||
| [JsonProperty("max_age")] | [JsonProperty("max_age")] | ||||
| public int MaxAge { get; set; } = 86400; //24 Hours | |||||
| public Optional<int> MaxAge { get; set; } | |||||
| [JsonProperty("max_uses")] | [JsonProperty("max_uses")] | ||||
| public int MaxUses { get; set; } = 0; | |||||
| public Optional<int> MaxUses { get; set; } | |||||
| [JsonProperty("temporary")] | [JsonProperty("temporary")] | ||||
| public bool Temporary { get; set; } = false; | |||||
| public Optional<bool> Temporary { get; set; } | |||||
| [JsonProperty("xkcdpass")] | [JsonProperty("xkcdpass")] | ||||
| public bool XkcdPass { get; set; } = false; | |||||
| public Optional<bool> XkcdPass { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||||
| public class CreateGuildBanParams | public class CreateGuildBanParams | ||||
| { | { | ||||
| [JsonProperty("delete-message-days")] | [JsonProperty("delete-message-days")] | ||||
| public int PruneDays { get; set; } = 0; | |||||
| public Optional<int> PruneDays { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,7 +8,8 @@ namespace Discord.API.Rest | |||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("type")] | [JsonProperty("type")] | ||||
| public ChannelType Type { get; set; } | public ChannelType Type { get; set; } | ||||
| [JsonProperty("bitrate")] | [JsonProperty("bitrate")] | ||||
| public int Bitrate { get; set; } | |||||
| public Optional<int> Bitrate { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -10,7 +10,8 @@ namespace Discord.API.Rest | |||||
| public string Name { get; set; } | public string Name { get; set; } | ||||
| [JsonProperty("region")] | [JsonProperty("region")] | ||||
| public string Region { get; set; } | public string Region { get; set; } | ||||
| [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | ||||
| public Stream Icon { get; set; } | |||||
| public Optional<Stream> Icon { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,9 +6,10 @@ namespace Discord.API.Rest | |||||
| { | { | ||||
| [JsonProperty("content")] | [JsonProperty("content")] | ||||
| public string Content { get; set; } = ""; | public string Content { get; set; } = ""; | ||||
| [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | [JsonProperty("nonce", NullValueHandling = NullValueHandling.Ignore)] | ||||
| public string Nonce { get; set; } = null; | |||||
| public Optional<string> Nonce { get; set; } | |||||
| [JsonProperty("tts", DefaultValueHandling = DefaultValueHandling.Ignore)] | [JsonProperty("tts", DefaultValueHandling = DefaultValueHandling.Ignore)] | ||||
| public bool IsTTS { get; set; } = false; | |||||
| public Optional<bool> IsTTS { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,6 +4,7 @@ | |||||
| { | { | ||||
| public int Limit { get; set; } = DiscordConfig.MaxMessagesPerBatch; | public int Limit { get; set; } = DiscordConfig.MaxMessagesPerBatch; | ||||
| public Direction RelativeDirection { get; set; } = Direction.Before; | public Direction RelativeDirection { get; set; } = Direction.Before; | ||||
| public ulong? RelativeMessageId { get; set; } = null; | |||||
| public Optional<ulong> RelativeMessageId { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -2,7 +2,7 @@ | |||||
| { | { | ||||
| public class GetGuildMembersParams | public class GetGuildMembersParams | ||||
| { | { | ||||
| public int? Limit { get; set; } = null; | |||||
| public int Offset { get; set; } = 0; | |||||
| public Optional<int> Limit { get; set; } | |||||
| public Optional<int> Offset { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,8 +5,8 @@ namespace Discord.API.Rest | |||||
| public class ModifyChannelPermissionsParams | public class ModifyChannelPermissionsParams | ||||
| { | { | ||||
| [JsonProperty("allow")] | [JsonProperty("allow")] | ||||
| public uint Allow { get; set; } | |||||
| public Optional<uint> Allow { get; set; } | |||||
| [JsonProperty("deny")] | [JsonProperty("deny")] | ||||
| public uint Deny { get; set; } | |||||
| public Optional<uint> Deny { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -7,14 +7,14 @@ namespace Discord.API.Rest | |||||
| public class ModifyCurrentUserParams | public class ModifyCurrentUserParams | ||||
| { | { | ||||
| [JsonProperty("username")] | [JsonProperty("username")] | ||||
| public string Username { get; set; } | |||||
| public Optional<string> Username { get; set; } | |||||
| [JsonProperty("email")] | [JsonProperty("email")] | ||||
| public string Email { get; set; } | |||||
| public Optional<string> Email { get; set; } | |||||
| [JsonProperty("password")] | [JsonProperty("password")] | ||||
| public string Password { get; set; } | |||||
| public Optional<string> Password { get; set; } | |||||
| [JsonProperty("new_password")] | [JsonProperty("new_password")] | ||||
| public string NewPassword { get; set; } | |||||
| public Optional<string> NewPassword { get; set; } | |||||
| [JsonProperty("avatar"), JsonConverter(typeof(ImageConverter))] | [JsonProperty("avatar"), JsonConverter(typeof(ImageConverter))] | ||||
| public Stream Avatar { get; set; } | |||||
| public Optional<Stream> Avatar { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,8 +5,8 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildChannelParams | public class ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | |||||
| public Optional<string> Name { get; set; } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public int Position { get; set; } | |||||
| public Optional<int> Position { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,8 +5,8 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildChannelsParams | public class ModifyGuildChannelsParams | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | |||||
| public Optional<ulong> Id { get; set; } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public int Position { get; set; } | |||||
| public Optional<int> Position { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -6,8 +6,8 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildEmbedParams | public class ModifyGuildEmbedParams | ||||
| { | { | ||||
| [JsonProperty("enabled")] | [JsonProperty("enabled")] | ||||
| public bool Enabled { get; set; } | |||||
| [JsonProperty("channel"), JsonConverter(typeof(UInt64EntityConverter))] | |||||
| public IVoiceChannel Channel { get; set; } | |||||
| public Optional<bool> Enabled { get; set; } | |||||
| [JsonProperty("channel")] | |||||
| public Optional<IVoiceChannel> Channel { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,10 +5,10 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildIntegrationParams | public class ModifyGuildIntegrationParams | ||||
| { | { | ||||
| [JsonProperty("expire_behavior")] | [JsonProperty("expire_behavior")] | ||||
| public int ExpireBehavior { get; set; } | |||||
| public Optional<int> ExpireBehavior { get; set; } | |||||
| [JsonProperty("expire_grace_period")] | [JsonProperty("expire_grace_period")] | ||||
| public int ExpireGracePeriod { get; set; } | |||||
| public Optional<int> ExpireGracePeriod { get; set; } | |||||
| [JsonProperty("enable_emoticons")] | [JsonProperty("enable_emoticons")] | ||||
| public bool EnableEmoticons { get; set; } | |||||
| public Optional<bool> EnableEmoticons { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,17 +1,18 @@ | |||||
| using Discord.Net.Converters; | |||||
| using Newtonsoft.Json; | |||||
| using Newtonsoft.Json; | |||||
| namespace Discord.API.Rest | namespace Discord.API.Rest | ||||
| { | { | ||||
| public class ModifyGuildMemberParams | public class ModifyGuildMemberParams | ||||
| { | { | ||||
| [JsonProperty("roles")] | [JsonProperty("roles")] | ||||
| public ulong[] Roles { get; set; } | |||||
| public Optional<ulong[]> Roles { get; set; } | |||||
| [JsonProperty("mute")] | [JsonProperty("mute")] | ||||
| public bool Mute { get; set; } | |||||
| public Optional<bool> Mute { get; set; } | |||||
| [JsonProperty("deaf")] | [JsonProperty("deaf")] | ||||
| public bool Deaf { get; set; } | |||||
| [JsonProperty("channel_id"), JsonConverter(typeof(UInt64EntityConverter))] | |||||
| public IVoiceChannel VoiceChannel { get; set; } | |||||
| public Optional<bool> Deaf { get; set; } | |||||
| [JsonProperty("nick")] | |||||
| public Optional<string> Nickname { get; set; } | |||||
| [JsonProperty("channel_id")] | |||||
| public Optional<IVoiceChannel> VoiceChannel { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -7,20 +7,20 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildParams | public class ModifyGuildParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | |||||
| [JsonProperty("region"), JsonConverterAttribute(typeof(StringEntityConverter))] | |||||
| public IVoiceRegion Region { get; set; } | |||||
| public Optional<string> Name { get; set; } | |||||
| [JsonProperty("region")] | |||||
| public Optional<IVoiceRegion> Region { get; set; } | |||||
| [JsonProperty("verification_level")] | [JsonProperty("verification_level")] | ||||
| public int VerificationLevel { get; set; } | |||||
| public Optional<int> VerificationLevel { get; set; } | |||||
| [JsonProperty("afk_channel_id")] | [JsonProperty("afk_channel_id")] | ||||
| public ulong? AFKChannelId { get; set; } | |||||
| public Optional<ulong?> AFKChannelId { get; set; } | |||||
| [JsonProperty("afk_timeout")] | [JsonProperty("afk_timeout")] | ||||
| public int AFKTimeout { get; set; } | |||||
| public Optional<int> AFKTimeout { get; set; } | |||||
| [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | [JsonProperty("icon"), JsonConverter(typeof(ImageConverter))] | ||||
| public Stream Icon { get; set; } | |||||
| public Optional<Stream> Icon { get; set; } | |||||
| [JsonProperty("owner_id")] | [JsonProperty("owner_id")] | ||||
| public GuildMember Owner { get; set; } | |||||
| public Optional<GuildMember> Owner { get; set; } | |||||
| [JsonProperty("splash"), JsonConverter(typeof(ImageConverter))] | [JsonProperty("splash"), JsonConverter(typeof(ImageConverter))] | ||||
| public Stream Splash { get; set; } | |||||
| public Optional<Stream> Splash { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,14 +5,14 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildRoleParams | public class ModifyGuildRoleParams | ||||
| { | { | ||||
| [JsonProperty("name")] | [JsonProperty("name")] | ||||
| public string Name { get; set; } | |||||
| public Optional<string> Name { get; set; } | |||||
| [JsonProperty("permissions")] | [JsonProperty("permissions")] | ||||
| public uint Permissions { get; set; } | |||||
| public Optional<uint> Permissions { get; set; } | |||||
| [JsonProperty("position")] | [JsonProperty("position")] | ||||
| public int Position { get; set; } | |||||
| public Optional<int> Position { get; set; } | |||||
| [JsonProperty("color")] | [JsonProperty("color")] | ||||
| public uint Color { get; set; } | |||||
| public Optional<uint> Color { get; set; } | |||||
| [JsonProperty("hoist")] | [JsonProperty("hoist")] | ||||
| public bool Hoist { get; set; } | |||||
| public Optional<bool> Hoist { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||||
| public class ModifyGuildRolesParams : ModifyGuildRoleParams | public class ModifyGuildRolesParams : ModifyGuildRoleParams | ||||
| { | { | ||||
| [JsonProperty("id")] | [JsonProperty("id")] | ||||
| public ulong Id { get; set; } | |||||
| public Optional<ulong> Id { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||||
| public class ModifyMessageParams | public class ModifyMessageParams | ||||
| { | { | ||||
| [JsonProperty("content")] | [JsonProperty("content")] | ||||
| public string Content { get; set; } = ""; | |||||
| public Optional<string> Content { get; set; } = ""; | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||||
| public class ModifyTextChannelParams : ModifyGuildChannelParams | public class ModifyTextChannelParams : ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("topic")] | [JsonProperty("topic")] | ||||
| public string Topic { get; set; } | |||||
| public Optional<string> Topic { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,6 @@ namespace Discord.API.Rest | |||||
| public class ModifyVoiceChannelParams : ModifyGuildChannelParams | public class ModifyVoiceChannelParams : ModifyGuildChannelParams | ||||
| { | { | ||||
| [JsonProperty("bitrate")] | [JsonProperty("bitrate")] | ||||
| public int Bitrate { get; set; } | |||||
| public Optional<int> Bitrate { get; set; } | |||||
| } | } | ||||
| } | } | ||||
| @@ -4,21 +4,22 @@ namespace Discord.API.Rest | |||||
| { | { | ||||
| public class UploadFileParams | public class UploadFileParams | ||||
| { | { | ||||
| public string Content { get; set; } = ""; | |||||
| public string Nonce { get; set; } = null; | |||||
| public bool IsTTS { get; set; } = false; | |||||
| public string Filename { get; set; } = "unknown.dat"; | public string Filename { get; set; } = "unknown.dat"; | ||||
| public Optional<string> Content { get; set; } | |||||
| public Optional<string> Nonce { get; set; } | |||||
| public Optional<bool> IsTTS { get; set; } | |||||
| public IReadOnlyDictionary<string, string> ToDictionary() | public IReadOnlyDictionary<string, string> ToDictionary() | ||||
| { | { | ||||
| var dic = new Dictionary<string, string> | |||||
| { | |||||
| ["content"] = Content, | |||||
| ["tts"] = IsTTS.ToString() | |||||
| }; | |||||
| if (Nonce != null) | |||||
| dic.Add("nonce", Nonce); | |||||
| return dic; | |||||
| var d = new Dictionary<string, string>(); | |||||
| if (Content.IsSpecified) | |||||
| d["content"] = Content.Value; | |||||
| if (IsTTS.IsSpecified) | |||||
| d["tts"] = IsTTS.Value.ToString(); | |||||
| if (Nonce.IsSpecified) | |||||
| d["nonce"] = Nonce.Value; | |||||
| return d; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -90,7 +90,9 @@ namespace Discord | |||||
| /// <summary> Gets a collection of all users in this guild. </summary> | /// <summary> Gets a collection of all users in this guild. </summary> | ||||
| Task<IEnumerable<IGuildUser>> GetUsers(); | Task<IEnumerable<IGuildUser>> GetUsers(); | ||||
| /// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | /// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | ||||
| Task<IGuildUser> GetUser(ulong id); | |||||
| Task<IGuildUser> GetUser(ulong id); | |||||
| /// <summary> Gets the current user for this guild. </summary> | |||||
| Task<IGuildUser> GetCurrentUser(); | |||||
| Task<int> PruneUsers(int days = 30, bool simulate = false); | Task<int> PruneUsers(int days = 30, bool simulate = false); | ||||
| } | } | ||||
| } | } | ||||
| @@ -12,6 +12,8 @@ namespace Discord | |||||
| ushort Discriminator { get; } | ushort Discriminator { get; } | ||||
| /// <summary> Returns true if this user is a bot account. </summary> | /// <summary> Returns true if this user is a bot account. </summary> | ||||
| bool IsBot { get; } | bool IsBot { get; } | ||||
| /// <summary> Returns true is this user is the current logged-in account. </summary> | |||||
| bool IsCurrentUser { get; } | |||||
| /// <summary> Gets the current status of this user. </summary> | /// <summary> Gets the current status of this user. </summary> | ||||
| UserStatus Status { get; } | UserStatus Status { get; } | ||||
| /// <summary> Gets the username for this user. </summary> | /// <summary> Gets the username for this user. </summary> | ||||
| @@ -66,6 +66,8 @@ | |||||
| <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\IOptional.cs" /> | |||||
| <Compile Include="API\Optional.cs" /> | |||||
| <Compile Include="API\Rest\DeleteMessagesParam.cs" /> | <Compile Include="API\Rest\DeleteMessagesParam.cs" /> | ||||
| <Compile Include="API\Rest\GetGuildMembersParams.cs" /> | <Compile Include="API\Rest\GetGuildMembersParams.cs" /> | ||||
| <Compile Include="API\Rest\UploadFileParams.cs" /> | <Compile Include="API\Rest\UploadFileParams.cs" /> | ||||
| @@ -95,6 +97,8 @@ | |||||
| <Compile Include="API\Rest\ModifyVoiceChannelParams.cs" /> | <Compile Include="API\Rest\ModifyVoiceChannelParams.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="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" /> | ||||
| <Compile Include="Net\Rest\RequestQueue\GlobalBucket.cs" /> | <Compile Include="Net\Rest\RequestQueue\GlobalBucket.cs" /> | ||||
| @@ -1,4 +1,5 @@ | |||||
| using Newtonsoft.Json; | |||||
| using Discord.API; | |||||
| using Newtonsoft.Json; | |||||
| using System; | using System; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -6,7 +7,7 @@ namespace Discord.Net.Converters | |||||
| { | { | ||||
| public class ImageConverter : JsonConverter | public class ImageConverter : JsonConverter | ||||
| { | { | ||||
| public override bool CanConvert(Type objectType) => objectType == typeof(Stream); | |||||
| public override bool CanConvert(Type objectType) => objectType == typeof(Stream) || objectType == typeof(Optional<Stream>); | |||||
| public override bool CanRead => true; | public override bool CanRead => true; | ||||
| public override bool CanWrite => true; | public override bool CanWrite => true; | ||||
| @@ -17,6 +18,8 @@ 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 is Optional<Stream>) | |||||
| value = (Optional<Stream>)value; | |||||
| var stream = value as Stream; | var stream = value as Stream; | ||||
| byte[] bytes = new byte[stream.Length - stream.Position]; | byte[] bytes = new byte[stream.Length - stream.Position]; | ||||
| @@ -0,0 +1,34 @@ | |||||
| 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; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| using Discord.API; | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| namespace Discord.Net.Converters | |||||
| { | |||||
| public class OptionalConverter : JsonConverter | |||||
| { | |||||
| public override bool CanConvert(Type objectType) => objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Optional<>); | |||||
| public override bool CanRead => false; | |||||
| public override bool CanWrite => true; | |||||
| public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |||||
| { | |||||
| throw new InvalidOperationException(); | |||||
| } | |||||
| public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |||||
| { | |||||
| serializer.Serialize(writer, (value as IOptional).Value); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -1,5 +1,4 @@ | |||||
| using Newtonsoft.Json; | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Globalization; | using System.Globalization; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -33,6 +32,8 @@ namespace Discord.Net.Rest | |||||
| UseProxy = false, | UseProxy = false, | ||||
| PreAuthenticate = false | PreAuthenticate = false | ||||
| }); | }); | ||||
| SetHeader("accept-encoding", "gzip, deflate"); | |||||
| } | } | ||||
| protected virtual void Dispose(bool disposing) | protected virtual void Dispose(bool disposing) | ||||
| { | { | ||||
| @@ -54,18 +55,18 @@ namespace Discord.Net.Rest | |||||
| _client.DefaultRequestHeaders.Add(key, value); | _client.DefaultRequestHeaders.Add(key, value); | ||||
| } | } | ||||
| public async Task<Stream> Send(string method, string endpoint, string json = null) | |||||
| public async Task<Stream> Send(string method, string endpoint, string json = null, bool headerOnly = false) | |||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | ||||
| { | { | ||||
| if (json != null) | if (json != null) | ||||
| restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); | restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json"); | ||||
| return await SendInternal(restRequest, _cancelToken).ConfigureAwait(false); | |||||
| return await SendInternal(restRequest, _cancelToken, headerOnly).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| public async Task<Stream> Send(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams) | |||||
| public async Task<Stream> Send(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false) | |||||
| { | { | ||||
| string uri = Path.Combine(_baseUrl, endpoint); | string uri = Path.Combine(_baseUrl, endpoint); | ||||
| using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | using (var restRequest = new HttpRequestMessage(GetMethod(method), uri)) | ||||
| @@ -95,11 +96,11 @@ namespace Discord.Net.Rest | |||||
| } | } | ||||
| } | } | ||||
| restRequest.Content = content; | restRequest.Content = content; | ||||
| return await SendInternal(restRequest, _cancelToken).ConfigureAwait(false); | |||||
| return await SendInternal(restRequest, _cancelToken, headerOnly).ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| private async Task<Stream> SendInternal(HttpRequestMessage request, CancellationToken cancelToken) | |||||
| private async Task<Stream> SendInternal(HttpRequestMessage request, CancellationToken cancelToken, bool headerOnly) | |||||
| { | { | ||||
| int retryCount = 0; | int retryCount = 0; | ||||
| while (true) | while (true) | ||||
| @@ -125,7 +126,10 @@ namespace Discord.Net.Rest | |||||
| throw new HttpException(response.StatusCode); | throw new HttpException(response.StatusCode); | ||||
| } | } | ||||
| return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | |||||
| if (headerOnly) | |||||
| return null; | |||||
| else | |||||
| return await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -8,7 +8,7 @@ namespace Discord.Net.Rest | |||||
| { | { | ||||
| void SetHeader(string key, string value); | void SetHeader(string key, string value); | ||||
| Task<Stream> Send(string method, string endpoint, string json = null); | |||||
| Task<Stream> Send(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams); | |||||
| Task<Stream> Send(string method, string endpoint, string json = null, bool headerOnly = false); | |||||
| Task<Stream> Send(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly = false); | |||||
| } | } | ||||
| } | } | ||||
| @@ -83,9 +83,9 @@ namespace Discord.Net.Rest | |||||
| { | { | ||||
| Stream stream; | Stream stream; | ||||
| if (request.IsMultipart) | if (request.IsMultipart) | ||||
| stream = await _parent.RestClient.Send(request.Method, request.Endpoint, request.MultipartParams).ConfigureAwait(false); | |||||
| stream = await _parent.RestClient.Send(request.Method, request.Endpoint, request.MultipartParams, request.HeaderOnly).ConfigureAwait(false); | |||||
| else | else | ||||
| stream = await _parent.RestClient.Send(request.Method, request.Endpoint, request.Json).ConfigureAwait(false); | |||||
| stream = await _parent.RestClient.Send(request.Method, request.Endpoint, request.Json, request.HeaderOnly).ConfigureAwait(false); | |||||
| request.Promise.SetResult(stream); | request.Promise.SetResult(stream); | ||||
| } | } | ||||
| catch (HttpRateLimitException ex) //Preemptive check failed, use Discord's time instead of our own | catch (HttpRateLimitException ex) //Preemptive check failed, use Discord's time instead of our own | ||||
| @@ -9,29 +9,31 @@ namespace Discord.Net.Rest | |||||
| public string Method { get; } | public string Method { get; } | ||||
| public string Endpoint { get; } | public string Endpoint { get; } | ||||
| public string Json { get; } | public string Json { get; } | ||||
| public bool HeaderOnly { get; } | |||||
| public IReadOnlyDictionary<string, object> MultipartParams { get; } | public IReadOnlyDictionary<string, object> MultipartParams { get; } | ||||
| public TaskCompletionSource<Stream> Promise { get; } | public TaskCompletionSource<Stream> Promise { get; } | ||||
| public bool IsMultipart => MultipartParams != null; | public bool IsMultipart => MultipartParams != null; | ||||
| public RestRequest(string method, string endpoint, string json) | |||||
| : this(method, endpoint) | |||||
| public RestRequest(string method, string endpoint, string json, bool headerOnly) | |||||
| : this(method, endpoint, headerOnly) | |||||
| { | { | ||||
| Json = json; | Json = json; | ||||
| } | } | ||||
| public RestRequest(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams) | |||||
| : this(method, endpoint) | |||||
| public RestRequest(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, bool headerOnly) | |||||
| : this(method, endpoint, headerOnly) | |||||
| { | { | ||||
| MultipartParams = multipartParams; | MultipartParams = multipartParams; | ||||
| } | } | ||||
| private RestRequest(string method, string endpoint) | |||||
| private RestRequest(string method, string endpoint, bool headerOnly) | |||||
| { | { | ||||
| Method = method; | Method = method; | ||||
| Endpoint = endpoint; | Endpoint = endpoint; | ||||
| Json = null; | Json = null; | ||||
| MultipartParams = null; | MultipartParams = null; | ||||
| HeaderOnly = headerOnly; | |||||
| Promise = new TaskCompletionSource<Stream>(); | Promise = new TaskCompletionSource<Stream>(); | ||||
| } | } | ||||
| } | } | ||||
| @@ -306,7 +306,6 @@ namespace Discord.Rest | |||||
| var models = await Discord.BaseClient.GetGuildMembers(Id, args).ConfigureAwait(false); | var models = await Discord.BaseClient.GetGuildMembers(Id, args).ConfigureAwait(false); | ||||
| return models.Select(x => new GuildUser(this, x)); | return models.Select(x => new GuildUser(this, x)); | ||||
| } | } | ||||
| /// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | /// <summary> Gets the user in this guild with the provided id, or null if not found. </summary> | ||||
| public async Task<GuildUser> GetUser(ulong id) | public async Task<GuildUser> GetUser(ulong id) | ||||
| { | { | ||||
| @@ -315,7 +314,11 @@ namespace Discord.Rest | |||||
| return new GuildUser(this, model); | return new GuildUser(this, model); | ||||
| return null; | return null; | ||||
| } | } | ||||
| /// <summary> Gets a the current user. </summary> | |||||
| public async Task<GuildUser> GetCurrentUser() | |||||
| { | |||||
| return await GetUser(Discord.CurrentUser.Id).ConfigureAwait(false); | |||||
| } | |||||
| public async Task<int> PruneUsers(int days = 30, bool simulate = false) | public async Task<int> PruneUsers(int days = 30, bool simulate = false) | ||||
| { | { | ||||
| var args = new GuildPruneParams() { Days = days }; | var args = new GuildPruneParams() { Days = days }; | ||||
| @@ -367,6 +370,8 @@ namespace Discord.Rest | |||||
| => Task.FromResult<IEnumerable<IRole>>(Roles); | => Task.FromResult<IEnumerable<IRole>>(Roles); | ||||
| async Task<IGuildUser> IGuild.GetUser(ulong id) | async Task<IGuildUser> IGuild.GetUser(ulong id) | ||||
| => await GetUser(id).ConfigureAwait(false); | => await GetUser(id).ConfigureAwait(false); | ||||
| async Task<IGuildUser> IGuild.GetCurrentUser() | |||||
| => await GetCurrentUser().ConfigureAwait(false); | |||||
| async Task<IEnumerable<IGuildUser>> IGuild.GetUsers() | async Task<IEnumerable<IGuildUser>> IGuild.GetUsers() | ||||
| => await GetUsers().ConfigureAwait(false); | => await GetUsers().ConfigureAwait(false); | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.Linq; | |||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||
| using Model = Discord.API.GuildMember; | using Model = Discord.API.GuildMember; | ||||
| @@ -82,8 +83,15 @@ namespace Discord.Rest | |||||
| var args = new ModifyGuildMemberParams(); | var args = new ModifyGuildMemberParams(); | ||||
| func(args); | func(args); | ||||
| var model = await Discord.BaseClient.ModifyGuildMember(Guild.Id, Id, args).ConfigureAwait(false); | |||||
| Update(model); | |||||
| await Discord.BaseClient.ModifyGuildMember(Guild.Id, Id, args).ConfigureAwait(false); | |||||
| if (args.Deaf.IsSpecified) | |||||
| IsDeaf = args.Deaf; | |||||
| if (args.Mute.IsSpecified) | |||||
| IsMute = args.Mute; | |||||
| if (args.Nickname.IsSpecified) | |||||
| Nickname = args.Nickname; | |||||
| if (args.Roles.IsSpecified) | |||||
| _roles = args.Roles.Value.Select(x => Guild.GetRole(x)).Where(x => x != null).ToImmutableArray(); | |||||
| } | } | ||||
| @@ -28,6 +28,8 @@ namespace Discord.Rest | |||||
| public string Mention => MentionHelper.Mention(this, false); | public string Mention => MentionHelper.Mention(this, false); | ||||
| /// <inheritdoc /> | /// <inheritdoc /> | ||||
| public string NicknameMention => MentionHelper.Mention(this, true); | public string NicknameMention => MentionHelper.Mention(this, true); | ||||
| /// <inheritdoc /> | |||||
| public bool IsCurrentUser => Id == Discord.CurrentUser.Id; | |||||
| internal User(Model model) | internal User(Model model) | ||||
| { | { | ||||