@@ -5,6 +5,7 @@ using Discord.Net.Converters;
using Discord.Net.Queue;
using Discord.Net.Rest;
using Discord.Net.WebSockets;
using Discord.Rest;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
@@ -21,133 +22,96 @@ using System.Threading.Tasks;
namespace Discord.API
{
public class DiscordApiClient : IDisposable
public class DiscordRest ApiClient : IDisposable
{
private object _eventLock = new object();
public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } }
private readonly AsyncEvent<Func<string, string, double, Task>> _sentRequestEvent = new AsyncEvent<Func<string, string, double, Task>>();
public event Func<GatewayOpCode, Task> SentGatewayMessage { add { _sentGatewayMessageEvent.Add(value); } remove { _sentGatewayMessageEvent.Remove(value); } }
private readonly AsyncEvent<Func<GatewayOpCode, Task>> _sentGatewayMessageEvent = new AsyncEvent<Func<GatewayOpCode, Task>>();
public event Func<GatewayOpCode, int?, string, object, Task> ReceivedGatewayEvent { add { _receivedGatewayEvent.Add(value); } remove { _receivedGatewayEvent.Remove(value); } }
private readonly AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>> _receivedGatewayEvent = new AsyncEvent<Func<GatewayOpCode, int?, string, object, Task>>();
public event Func<Exception, Task> Disconnected { add { _disconnectedEvent.Add(value); } remove { _disconnectedEvent.Remove(value); } }
private readonly AsyncEvent<Func<Exception, Task>> _disconnectedEvent = new AsyncEvent<Func<Exception, Task>>();
private readonly RequestQueue _requestQueue;
private readonly JsonSerializer _serializer;
private readonly IRestClient _restClient;
private readonly IWebSocketClient _gatewayClient;
private readonly SemaphoreSlim _connectionLock;
private CancellationTokenSource _loginCancelToken, _connectCancelToken;
private string _authToken;
private string _gatewayUrl;
private bool _isDisposed;
protected readonly JsonSerializer _serializer;
protected readonly SemaphoreSlim _stateLock;
private readonly RestClientProvider _restClientProvider;
protected string _authToken;
protected bool _isDisposed;
private CancellationTokenSource _loginCancelToken;
private IRestClient _restClient;
public LoginState LoginState { get; private set; }
public ConnectionState ConnectionState { get; private set; }
public TokenType AuthTokenType { get; private set; }
internal RequestQueue RequestQueue { get; private set; }
public DiscordApiClient(RestClientProvider restClientProvider, WebSocketProvider webSocketProvider = null , JsonSerializer serializer = null, RequestQueue requestQueue = null)
public DiscordRestApiClient(RestClientProvider restClientProvider, JsonSerializer serializer = null, RequestQueue requestQueue = null)
{
_connectionLock = new SemaphoreSlim(1, 1);
_restClientProvider = restClientProvider;
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
RequestQueue = requestQueue;
_requestQueue = requestQueue ?? new RequestQueue();
_stateLock = new SemaphoreSlim(1, 1 );
_restClient = restClientProvider(DiscordRestConfig.ClientAPIUrl);
SetBaseUrl(DiscordConfig.ClientAPIUrl);
}
internal void SetBaseUrl(string baseUrl)
{
_restClient = _restClientProvider(baseUrl);
_restClient.SetHeader("accept", "*/*");
_restClient.SetHeader("user-agent", DiscordRestConfig.UserAgent);
if (webSocketProvider != null)
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));
}
internal static string GetPrefixedToken(TokenType tokenType, string token)
{
switch (tokenType)
{
_gatewayClient = webSocketProvider();
//_gatewayClient.SetHeader("user-agent", DiscordConfig.UserAgent); (Causes issues in .Net 4.6+)
_gatewayClient.BinaryMessage += async (data, index, count) =>
{
using (var compressed = new MemoryStream(data, index + 2, count - 2))
using (var decompressed = new MemoryStream())
{
using (var zlib = new DeflateStream(compressed, CompressionMode.Decompress))
zlib.CopyTo(decompressed);
decompressed.Position = 0;
using (var reader = new StreamReader(decompressed))
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(reader.ReadToEnd());
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
}
}
};
_gatewayClient.TextMessage += async text =>
{
var msg = JsonConvert.DeserializeObject<WebSocketMessage>(text);
await _receivedGatewayEvent.InvokeAsync((GatewayOpCode)msg.Operation, msg.Sequence, msg.Type, msg.Payload).ConfigureAwait(false);
};
_gatewayClient.Closed += async ex =>
{
await DisconnectAsync().ConfigureAwait(false);
await _disconnectedEvent.InvokeAsync(ex).ConfigureAwait(false);
};
case TokenType.Bot:
return $"Bot {token}";
case TokenType.Bearer:
return $"Bearer {token}";
case TokenType.User:
return token;
default:
throw new ArgumentException("Unknown OAuth token type", nameof(tokenType));
}
_serializer = serializer ?? new JsonSerializer { ContractResolver = new DiscordContractResolver() };
}
private void Dispose(bool disposing)
internal virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
_loginCancelToken?.Dispose();
_connectCancelToken?.Dispose();
(_restClient as IDisposable)?.Dispose();
(_gatewayClient as IDisposable)?.Dispose();
}
_isDisposed = true;
}
}
public void Dispose() => Dispose(true);
public async Task LoginAsync(TokenType tokenType, string token, RequestOptions options = null)
{
await _connection Lock.WaitAsync().ConfigureAwait(false);
await _stateLock.WaitAsync().ConfigureAwait(false);
try
{
await LoginInternalAsync(tokenType, token, options).ConfigureAwait(false);
}
finally { _connection Lock.Release(); }
finally { _state Lock.Release(); }
}
private async Task LoginInternalAsync(TokenType tokenType, string token, RequestOptions options = null)
{
if (LoginState != LoginState.LoggedOut)
await LogoutInternalAsync().ConfigureAwait(false);
LoginState = LoginState.LoggingIn;
try
{
_loginCancelToken = new CancellationTokenSource();
AuthTokenType = TokenType.User;
_authToken = null;
_restClient.SetHeader("authorization", null);
await _requestQueue.SetCancelTokenAsync(_loginCancelToken.Token).ConfigureAwait(false);
await RequestQueue.SetCancelTokenAsync(_loginCancelToken.Token).ConfigureAwait(false);
_restClient.SetCancelToken(_loginCancelToken.Token);
AuthTokenType = tokenType;
_authToken = token;
switch (tokenType)
{
case TokenType.Bot:
token = $"Bot {token}";
break;
case TokenType.Bearer:
token = $"Bearer {token}";
break;
case TokenType.User:
break;
default:
throw new ArgumentException("Unknown oauth token type", nameof(tokenType));
}
_restClient.SetHeader("authorization", token);
_restClient.SetHeader("authorization", GetPrefixedToken(AuthTokenType, _authToken));
LoginState = LoginState.LoggedIn;
}
@@ -160,129 +124,58 @@ namespace Discord.API
public async Task LogoutAsync()
{
await _connection Lock.WaitAsync().ConfigureAwait(false);
await _state Lock.WaitAsync().ConfigureAwait(false);
try
{
await LogoutInternalAsync().ConfigureAwait(false);
}
finally { _connection Lock.Release(); }
finally { _state Lock.Release(); }
}
private async Task LogoutInternalAsync()
{
//An exception here will lock the client into the unusable LoggingOut state, but that's probably fine since our client is in an undefined state too.
if (LoginState == LoginState.LoggedOut) return;
LoginState = LoginState.LoggingOut;
try { _loginCancelToken?.Cancel(false); }
catch { }
await DisconnectInternalAsync().ConfigureAwait(false);
await _r equestQueue.ClearAsync().ConfigureAwait(false);
await R equestQueue.ClearAsync().ConfigureAwait(false);
await _r equestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false);
await R equestQueue.SetCancelTokenAsync(CancellationToken.None).ConfigureAwait(false);
_restClient.SetCancelToken(CancellationToken.None);
LoginState = LoginState.LoggedOut;
}
public async Task ConnectAsync()
{
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
await ConnectInternalAsync().ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
}
private async Task ConnectInternalAsync()
{
if (LoginState != LoginState.LoggedIn)
throw new InvalidOperationException("You must log in before connecting.");
if (_gatewayClient == null)
throw new NotSupportedException("This client is not configured with websocket support.");
ConnectionState = ConnectionState.Connecting;
try
{
_connectCancelToken = new CancellationTokenSource();
if (_gatewayClient != null)
_gatewayClient.SetCancelToken(_connectCancelToken.Token);
if (_gatewayUrl == null)
{
var gatewayResponse = await GetGatewayAsync().ConfigureAwait(false);
_gatewayUrl = $"{gatewayResponse.Url}?v={DiscordConfig.APIVersion}&encoding={DiscordSocketConfig.GatewayEncoding}";
}
await _gatewayClient.ConnectAsync(_gatewayUrl).ConfigureAwait(false);
ConnectionState = ConnectionState.Connected;
}
catch (Exception)
{
_gatewayUrl = null; //Uncache in case the gateway url changed
await DisconnectInternalAsync().ConfigureAwait(false);
throw;
}
}
public async Task DisconnectAsync()
{
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
await DisconnectInternalAsync().ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
}
public async Task DisconnectAsync(Exception ex)
{
await _connectionLock.WaitAsync().ConfigureAwait(false);
try
{
await DisconnectInternalAsync().ConfigureAwait(false);
}
finally { _connectionLock.Release(); }
}
private async Task DisconnectInternalAsync()
{
if (_gatewayClient == null)
throw new NotSupportedException("This client is not configured with websocket support.");
if (ConnectionState == ConnectionState.Disconnected) return;
ConnectionState = ConnectionState.Disconnecting;
try { _connectCancelToken?.Cancel(false); }
catch { }
await _gatewayClient.DisconnectAsync().ConfigureAwait(false);
ConnectionState = ConnectionState.Disconnected;
}
internal virtual Task ConnectInternalAsync() => Task.CompletedTask;
internal virtual Task DisconnectInternalAsync() => Task.CompletedTask;
//REST
public Task SendAsync(string method, string endpoint,
public Task SendAsync(string method, string endpoint,
GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null)
=> SendInternalAsync(method, endpoint, null, true, BucketGroup.Global, (int)bucket, 0, options);
public Task SendAsync(string method, string endpoint, object payload,
public Task SendAsync(string method, string endpoint, object payload,
GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null)
=> SendInternalAsync(method, endpoint, payload, true, BucketGroup.Global, (int)bucket, 0, options);
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
GlobalBucket bucket = GlobalBucket.GeneralRest, RequestOptions options = null) where TResponse : class
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Global, (int)bucket, 0, options).ConfigureAwait(false));
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket =
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload, GlobalBucket bucket =
GlobalBucket.GeneralRest, RequestOptions options = null) where TResponse : class
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Global, (int)bucket, 0, options).ConfigureAwait(false));
public Task SendAsync(string method, string endpoint,
public Task SendAsync(string method, string endpoint,
GuildBucket bucket, ulong guildId, RequestOptions options = null)
=> SendInternalAsync(method, endpoint, null, true, BucketGroup.Guild, (int)bucket, guildId, options);
public Task SendAsync(string method, string endpoint, object payload,
public Task SendAsync(string method, string endpoint, object payload,
GuildBucket bucket, ulong guildId, RequestOptions options = null)
=> SendInternalAsync(method, endpoint, payload, true, BucketGroup.Guild, (int)bucket, guildId, options);
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint,
GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, null, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false));
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload,
public async Task<TResponse> SendAsync<TResponse>(string method, string endpoint, object payload,
GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class
=> DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, payload, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false));
@@ -301,24 +194,15 @@ namespace Discord.API
GuildBucket bucket, ulong guildId, RequestOptions options = null) where TResponse : class
=> DeserializeJson<TResponse>(await SendMultipartInternalAsync(method, endpoint, multipartArgs, false, BucketGroup.Guild, (int)bucket, guildId, options).ConfigureAwait(false));
//Gateway
public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
GlobalBucket bucket = GlobalBucket.GeneralGateway, RequestOptions options = null)
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Global, (int)bucket, 0, options);
public Task SendGatewayAsync(GatewayOpCode opCode, object payload,
GuildBucket bucket, ulong guildId, RequestOptions options = null)
=> SendGatewayInternalAsync(opCode, payload, BucketGroup.Guild, (int)bucket, guildId, options);
//Core
private async Task<Stream> SendInternalAsync(string method, string endpoint, object payload, bool headerOnly,
private async Task<Stream> SendInternalAsync(string method, string endpoint, object payload, bool headerOnly,
BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null)
{
var stopwatch = Stopwatch.StartNew();
string json = null;
if (payload != null)
json = SerializeJson(payload);
var responseStream = await _r equestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, json, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
var responseStream = await RequestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, json, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
stopwatch.Stop();
double milliseconds = ToMilliseconds(stopwatch);
@@ -326,11 +210,11 @@ namespace Discord.API
return responseStream;
}
private async Task<Stream> SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly,
private async Task<Stream> SendMultipartInternalAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartArgs, bool headerOnly,
BucketGroup group, int bucketId, ulong guildId, RequestOptions options = null)
{
var stopwatch = Stopwatch.StartNew();
var responseStream = await _r equestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, multipartArgs, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
var responseStream = await R equestQueue.SendAsync(new RestRequest(_restClient, method, endpoint, multipartArgs, headerOnly, options), group, bucketId, guildId).ConfigureAwait(false);
int bytes = headerOnly ? 0 : (int)responseStream.Length;
stopwatch.Stop();
@@ -339,23 +223,6 @@ namespace Discord.API
return responseStream;
}
private async Task SendGatewayInternalAsync(GatewayOpCode opCode, object payload,
BucketGroup group, int bucketId, ulong guildId, RequestOptions options)
{
//TODO: Add ETF
byte[] bytes = null;
payload = new WebSocketMessage { Operation = (int)opCode, Payload = payload };
if (payload != null)
bytes = Encoding.UTF8.GetBytes(SerializeJson(payload));
await _requestQueue.SendAsync(new WebSocketRequest(_gatewayClient, bytes, true, options), group, bucketId, guildId).ConfigureAwait(false);
await _sentGatewayMessageEvent.InvokeAsync(opCode).ConfigureAwait(false);
}
//Application
public async Task<Application> GetMyApplicationInfoAsync(RequestOptions options = null)
{
return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false);
}
//Auth
public async Task ValidateTokenAsync(RequestOptions options = null)
@@ -363,69 +230,6 @@ namespace Discord.API
await SendAsync("GET", "auth/login", options: options).ConfigureAwait(false);
}
//Gateway
public async Task<GetGatewayResponse> GetGatewayAsync(RequestOptions options = null)
{
return await SendAsync<GetGatewayResponse>("GET", "gateway", options: options).ConfigureAwait(false);
}
public async Task SendIdentifyAsync(int largeThreshold = 100, bool useCompression = true, RequestOptions options = null)
{
var props = new Dictionary<string, string>
{
["$device"] = "Discord.Net"
};
var msg = new IdentifyParams()
{
Token = _authToken,
Properties = props,
LargeThreshold = largeThreshold,
UseCompression = useCompression
};
await SendGatewayAsync(GatewayOpCode.Identify, msg, options: options).ConfigureAwait(false);
}
public async Task SendResumeAsync(string sessionId, int lastSeq, RequestOptions options = null)
{
var msg = new ResumeParams()
{
Token = _authToken,
SessionId = sessionId,
Sequence = lastSeq
};
await SendGatewayAsync(GatewayOpCode.Resume, msg, options: options).ConfigureAwait(false);
}
public async Task SendHeartbeatAsync(int lastSeq, RequestOptions options = null)
{
await SendGatewayAsync(GatewayOpCode.Heartbeat, lastSeq, options: options).ConfigureAwait(false);
}
public async Task SendStatusUpdateAsync(long? idleSince, Game game, RequestOptions options = null)
{
var args = new StatusUpdateParams
{
IdleSince = idleSince,
Game = game
};
await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false);
}
public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
}
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
{
var payload = new VoiceStateUpdateParams
{
GuildId = guildId,
ChannelId = channelId,
SelfDeaf = selfDeaf,
SelfMute = selfMute
};
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
}
public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{
await SendGatewayAsync(GatewayOpCode.GuildSync, guildIds, options: options).ConfigureAwait(false);
}
//Channels
public async Task<Channel> GetChannelAsync(ulong channelId, RequestOptions options = null)
{
@@ -791,13 +595,13 @@ namespace Discord.API
List<GuildMember[]> result;
if (args._limit.IsSpecified)
result = new List<GuildMember[]>((limit + DiscordRest Config.MaxUsersPerBatch - 1) / DiscordRest Config.MaxUsersPerBatch);
result = new List<GuildMember[]>((limit + DiscordConfig.MaxUsersPerBatch - 1) / DiscordConfig.MaxUsersPerBatch);
else
result = new List<GuildMember[]>();
while (true)
{
int runLimit = (limit >= DiscordRest Config.MaxUsersPerBatch) ? DiscordRest Config.MaxUsersPerBatch : limit;
int runLimit = (limit >= DiscordConfig.MaxUsersPerBatch) ? DiscordConfig.MaxUsersPerBatch : limit;
string endpoint = $"guilds/{guildId}/members?limit={runLimit}&after={afterUserId}";
var models = await SendAsync<GuildMember[]>("GET", endpoint, options: options).ConfigureAwait(false);
@@ -806,11 +610,11 @@ namespace Discord.API
result.Add(models);
limit -= DiscordRest Config.MaxUsersPerBatch;
limit -= DiscordConfig.MaxUsersPerBatch;
afterUserId = models[models.Length - 1].User.Id;
//Was this an incomplete (the last) batch?
if (models.Length != DiscordRest Config.MaxUsersPerBatch) break;
if (models.Length != DiscordConfig.MaxUsersPerBatch) break;
}
if (result.Count > 1)
@@ -919,15 +723,15 @@ namespace Discord.API
relativeDir = "around";
break;
}
int runs = (limit + DiscordRest Config.MaxMessagesPerBatch - 1) / DiscordRest Config.MaxMessagesPerBatch;
int lastRunCount = limit - (runs - 1) * DiscordRest Config.MaxMessagesPerBatch;
int runs = (limit + DiscordConfig.MaxMessagesPerBatch - 1) / DiscordConfig.MaxMessagesPerBatch;
int lastRunCount = limit - (runs - 1) * DiscordConfig.MaxMessagesPerBatch;
var result = new API.Message[runs][];
int i = 0;
for (; i < runs; i++)
{
int runCount = i == (runs - 1) ? lastRunCount : DiscordRest Config.MaxMessagesPerBatch;
int runCount = i == (runs - 1) ? lastRunCount : DiscordConfig.MaxMessagesPerBatch;
string endpoint;
if (relativeId != null)
endpoint = $"channels/{channelId}/messages?limit={runCount}&{relativeDir}={relativeId}";
@@ -966,7 +770,7 @@ namespace Discord.API
}
//Was this an incomplete (the last) batch?
if (models.Length != DiscordRest Config.MaxMessagesPerBatch) { i++; break; }
if (models.Length != DiscordConfig.MaxMessagesPerBatch) { i++; break; }
}
if (i > 1)
@@ -1011,8 +815,8 @@ namespace Discord.API
Preconditions.NotEqual(channelId, 0, nameof(channelId));
Preconditions.NotNull(args, nameof(args));
Preconditions.NotNullOrEmpty(args._content, nameof(args.Content));
if (args._content.Length > DiscordRest Config.MaxMessageSize)
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordRest Config.MaxMessageSize}.", nameof(args.Content));
if (args._content.Length > DiscordConfig.MaxMessageSize)
throw new ArgumentException($"Message content is too long, length must be less or equal to {DiscordConfig.MaxMessageSize}.", nameof(args.Content));
if (guildId != 0)
return await SendAsync<Message>("POST", $"channels/{channelId}/messages", args, GuildBucket.SendEditMessage, guildId, options: options).ConfigureAwait(false);
@@ -1033,15 +837,15 @@ namespace Discord.API
{
Preconditions.NotNull(args, nameof(args));
Preconditions.NotEqual(channelId, 0, nameof(channelId));
if (args._content.GetValueOrDefault(null) == null)
args._content = "";
else if (args._content.IsSpecified)
{
if (args._content.Value == null)
args._content = "";
if (args._content.Value?.Length > DiscordRest Config.MaxMessageSize)
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordRest Config.MaxMessageSize}.", nameof(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));
}
if (guildId != 0)
@@ -1121,8 +925,8 @@ namespace Discord.API
if (args._content.IsSpecified)
{
Preconditions.NotNullOrEmpty(args._content, nameof(args.Content));
if (args._content.Value.Length > DiscordRest Config.MaxMessageSize)
throw new ArgumentOutOfRangeException($"Message content is too long, length must be less or equal to {DiscordRest Config.MaxMessageSize}.", nameof(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));
}
if (guildId != 0)
@@ -1159,7 +963,7 @@ namespace Discord.API
{
Preconditions.NotNullOrEmpty(username, nameof(username));
Preconditions.NotNullOrEmpty(discriminator, nameof(discriminator));
try
{
var models = await QueryUsersAsync($"{username}#{discriminator}", 1, options: options).ConfigureAwait(false);
@@ -1176,7 +980,7 @@ namespace Discord.API
}
//Current User/DMs
public async Task<User> GetSelf Async(RequestOptions options = null)
public async Task<User> GetMyUser Async(RequestOptions options = null)
{
return await SendAsync<User>("GET", "users/@me", options: options).ConfigureAwait(false);
}
@@ -1192,6 +996,10 @@ namespace Discord.API
{
return await SendAsync<IReadOnlyCollection<UserGuild>>("GET", "users/@me/guilds", options: options).ConfigureAwait(false);
}
public async Task<Application> GetMyApplicationAsync(RequestOptions options = null)
{
return await SendAsync<Application>("GET", "oauth2/applications/@me", options: options).ConfigureAwait(false);
}
public async Task<User> ModifySelfAsync(ModifyCurrentUserParams args, RequestOptions options = null)
{
Preconditions.NotNull(args, nameof(args));
@@ -1227,8 +1035,8 @@ namespace Discord.API
}
//Helpers
private static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
private string SerializeJson(object value)
protected static double ToMilliseconds(Stopwatch stopwatch) => Math.Round((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0, 2);
protected string SerializeJson(object value)
{
var sb = new StringBuilder(256);
using (TextWriter text = new StringWriter(sb, CultureInfo.InvariantCulture))
@@ -1236,7 +1044,7 @@ namespace Discord.API
_serializer.Serialize(writer, value);
return sb.ToString();
}
private T DeserializeJson<T>(Stream jsonStream)
protected T DeserializeJson<T>(Stream jsonStream)
{
using (TextReader text = new StreamReader(jsonStream))
using (JsonReader reader = new JsonTextReader(text))