@@ -29,29 +29,13 @@ namespace Discord.API | |||||
public TokenType AuthTokenType { get; private set; } | public TokenType AuthTokenType { get; private set; } | ||||
public IRestClient RestClient { get; private set; } | public IRestClient RestClient { get; private set; } | ||||
public IRequestQueue RequestQueue { get; private set; } | public IRequestQueue RequestQueue { get; private set; } | ||||
internal DiscordRawClient(RestClientProvider restClientProvider, CancellationToken cancelToken, TokenType authTokenType, string authToken) | |||||
internal DiscordRawClient(RestClientProvider restClientProvider, CancellationToken cancelToken) | |||||
{ | { | ||||
_cancelToken = cancelToken; | _cancelToken = cancelToken; | ||||
AuthTokenType = authTokenType; | |||||
switch (authTokenType) | |||||
{ | |||||
case TokenType.Bot: | |||||
authToken = $"Bot {authToken}"; | |||||
break; | |||||
case TokenType.Bearer: | |||||
authToken = $"Bearer {authToken}"; | |||||
break; | |||||
case TokenType.User: | |||||
break; | |||||
default: | |||||
throw new ArgumentException("Unknown oauth token type", nameof(authTokenType)); | |||||
} | |||||
_restClient = restClientProvider(DiscordConfig.ClientAPIUrl, cancelToken); | _restClient = restClientProvider(DiscordConfig.ClientAPIUrl, cancelToken); | ||||
_restClient.SetHeader("accept", "*/*"); | _restClient.SetHeader("accept", "*/*"); | ||||
_restClient.SetHeader("authorization", authToken); | |||||
_restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | _restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | ||||
_requestQueue = new RequestQueue(_restClient); | _requestQueue = new RequestQueue(_restClient); | ||||
@@ -69,6 +53,27 @@ namespace Discord.API | |||||
_serializer.ContractResolver = new OptionalContractResolver(); | _serializer.ContractResolver = new OptionalContractResolver(); | ||||
} | } | ||||
public void SetToken(TokenType tokenType, string token) | |||||
{ | |||||
AuthTokenType = tokenType; | |||||
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); | |||||
} | |||||
//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, true, bucket); | => SendInternal(method, endpoint, null, true, bucket); | ||||
@@ -122,7 +127,7 @@ namespace Discord.API | |||||
stopwatch.Stop(); | stopwatch.Stop(); | ||||
double milliseconds = ToMilliseconds(stopwatch); | double milliseconds = ToMilliseconds(stopwatch); | ||||
SentRequest(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||||
SentRequest?.Invoke(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||||
return responseStream; | return responseStream; | ||||
} | } | ||||
@@ -134,11 +139,23 @@ namespace Discord.API | |||||
stopwatch.Stop(); | stopwatch.Stop(); | ||||
double milliseconds = ToMilliseconds(stopwatch); | double milliseconds = ToMilliseconds(stopwatch); | ||||
SentRequest(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||||
SentRequest?.Invoke(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||||
return responseStream; | return responseStream; | ||||
} | } | ||||
//Auth | |||||
public async Task Login(LoginParams args) | |||||
{ | |||||
var response = await Send<LoginResponse>("POST", "auth/login", args).ConfigureAwait(false); | |||||
SetToken(TokenType.User, response.Token); | |||||
} | |||||
public async Task ValidateToken() | |||||
{ | |||||
await Send("GET", "auth/login").ConfigureAwait(false); | |||||
} | |||||
//Gateway | //Gateway | ||||
public async Task<GetGatewayResponse> GetGateway() | public async Task<GetGatewayResponse> GetGateway() | ||||
{ | { | ||||
@@ -0,0 +1,12 @@ | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class LoginParams | |||||
{ | |||||
[JsonProperty("email")] | |||||
public string Email { get; set; } | |||||
[JsonProperty("password")] | |||||
public string Password { get; set; } | |||||
} | |||||
} |
@@ -0,0 +1,10 @@ | |||||
using Newtonsoft.Json; | |||||
namespace Discord.API.Rest | |||||
{ | |||||
public class LoginResponse | |||||
{ | |||||
[JsonProperty("token")] | |||||
public string Token { get; set; } | |||||
} | |||||
} |
@@ -70,6 +70,8 @@ | |||||
<Compile Include="API\Optional.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\LoginParams.cs" /> | |||||
<Compile Include="API\Rest\LoginResponse.cs" /> | |||||
<Compile Include="API\Rest\ModifyCurrentUserNickParams.cs" /> | <Compile Include="API\Rest\ModifyCurrentUserNickParams.cs" /> | ||||
<Compile Include="API\Rest\UploadFileParams.cs" /> | <Compile Include="API\Rest\UploadFileParams.cs" /> | ||||
<Compile Include="API\Rest\GuildPruneParams.cs" /> | <Compile Include="API\Rest\GuildPruneParams.cs" /> | ||||
@@ -14,7 +14,8 @@ namespace Discord | |||||
IRestClient RestClient { get; } | IRestClient RestClient { get; } | ||||
IRequestQueue RequestQueue { get; } | IRequestQueue RequestQueue { get; } | ||||
Task Login(TokenType tokenType, string token); | |||||
Task Login(string email, string password); | |||||
Task Login(TokenType tokenType, string token, bool validateToken = true); | |||||
Task Logout(); | Task Logout(); | ||||
Task<IChannel> GetChannel(ulong id); | Task<IChannel> GetChannel(ulong id); | ||||
@@ -47,43 +47,64 @@ namespace Discord.Rest | |||||
_log.Message += (s,e) => Log.Raise(this, e); | _log.Message += (s,e) => Log.Raise(this, e); | ||||
} | } | ||||
public async Task Login(TokenType tokenType, string token) | |||||
public async Task Login(string email, string password) | |||||
{ | { | ||||
await _connectionLock.WaitAsync().ConfigureAwait(false); | await _connectionLock.WaitAsync().ConfigureAwait(false); | ||||
try | try | ||||
{ | { | ||||
await LoginInternal(tokenType, token).ConfigureAwait(false); | |||||
await LoginInternal(email, password).ConfigureAwait(false); | |||||
} | } | ||||
finally { _connectionLock.Release(); } | finally { _connectionLock.Release(); } | ||||
} | } | ||||
private async Task LoginInternal(TokenType tokenType, string token) | |||||
public async Task Login(TokenType tokenType, string token, bool validateToken = true) | |||||
{ | |||||
await _connectionLock.WaitAsync().ConfigureAwait(false); | |||||
try | |||||
{ | |||||
await LoginInternal(tokenType, token, validateToken).ConfigureAwait(false); | |||||
} | |||||
finally { _connectionLock.Release(); } | |||||
} | |||||
private async Task LoginInternal(string email, string password) | |||||
{ | { | ||||
if (IsLoggedIn) | if (IsLoggedIn) | ||||
LogoutInternal(); | LogoutInternal(); | ||||
try | try | ||||
{ | { | ||||
var cancelTokenSource = new CancellationTokenSource(); | var cancelTokenSource = new CancellationTokenSource(); | ||||
BaseClient = new API.DiscordRawClient(_restClientProvider, cancelTokenSource.Token, tokenType, token); | |||||
BaseClient.SentRequest += (s, e) => _log.Verbose("Rest", $"{e.Method} {e.Endpoint}: {e.Milliseconds} ms"); | |||||
BaseClient = new API.DiscordRawClient(_restClientProvider, cancelTokenSource.Token); | |||||
//MessageQueue = new MessageQueue(RestClient, _restLogger); | |||||
//await MessageQueue.Start(_cancelTokenSource.Token).ConfigureAwait(false); | |||||
var args = new LoginParams { Email = email, Password = password }; | |||||
await BaseClient.Login(args).ConfigureAwait(false); | |||||
await CompleteLogin(cancelTokenSource, false).ConfigureAwait(false); | |||||
} | |||||
catch { LogoutInternal(); throw; } | |||||
} | |||||
private async Task LoginInternal(TokenType tokenType, string token, bool validateToken) | |||||
{ | |||||
if (IsLoggedIn) | |||||
LogoutInternal(); | |||||
try | |||||
{ | |||||
var cancelTokenSource = new CancellationTokenSource(); | |||||
BaseClient = new API.DiscordRawClient(_restClientProvider, cancelTokenSource.Token); | |||||
try | |||||
{ | |||||
var currentUser = await BaseClient.GetCurrentUser().ConfigureAwait(false); | |||||
_currentUser = new SelfUser(this, currentUser); | |||||
} | |||||
catch (HttpException ex) when (ex.StatusCode == HttpStatusCode.Unauthorized && tokenType == TokenType.Bearer) { } //Ignore 401 if Bearer doesnt have identity | |||||
_cancelTokenSource = cancelTokenSource; | |||||
IsLoggedIn = true; | |||||
LoggedIn.Raise(this); | |||||
BaseClient.SetToken(tokenType, token); | |||||
await CompleteLogin(cancelTokenSource, validateToken).ConfigureAwait(false); | |||||
} | } | ||||
catch { LogoutInternal(); throw; } | catch { LogoutInternal(); throw; } | ||||
} | } | ||||
private async Task CompleteLogin(CancellationTokenSource cancelTokenSource, bool validateToken) | |||||
{ | |||||
BaseClient.SentRequest += (s, e) => _log.Verbose("Rest", $"{e.Method} {e.Endpoint}: {e.Milliseconds} ms"); | |||||
if (validateToken) | |||||
await BaseClient.ValidateToken().ConfigureAwait(false); | |||||
_cancelTokenSource = cancelTokenSource; | |||||
IsLoggedIn = true; | |||||
LoggedIn.Raise(this); | |||||
} | |||||
public async Task Logout() | public async Task Logout() | ||||
{ | { | ||||
@@ -99,9 +120,14 @@ namespace Discord.Rest | |||||
{ | { | ||||
bool wasLoggedIn = IsLoggedIn; | bool wasLoggedIn = IsLoggedIn; | ||||
try { _cancelTokenSource.Cancel(false); } catch { } | |||||
if (_cancelTokenSource != null) | |||||
{ | |||||
try { _cancelTokenSource.Cancel(false); } | |||||
catch { } | |||||
} | |||||
BaseClient = null; | BaseClient = null; | ||||
_currentUser = null; | |||||
if (wasLoggedIn) | if (wasLoggedIn) | ||||
{ | { | ||||
@@ -87,10 +87,7 @@ namespace Discord.Rest | |||||
bool isCurrentUser = (await Discord.GetCurrentUser().ConfigureAwait(false)).Id == Id; | bool isCurrentUser = (await Discord.GetCurrentUser().ConfigureAwait(false)).Id == Id; | ||||
if (isCurrentUser && args.Nickname.IsSpecified) | if (isCurrentUser && args.Nickname.IsSpecified) | ||||
{ | { | ||||
var nickArgs = new ModifyCurrentUserNickParams | |||||
{ | |||||
Nickname = args.Nickname.Value | |||||
}; | |||||
var nickArgs = new ModifyCurrentUserNickParams { Nickname = args.Nickname.Value }; | |||||
await Discord.BaseClient.ModifyCurrentUserNick(Guild.Id, nickArgs).ConfigureAwait(false); | await Discord.BaseClient.ModifyCurrentUserNick(Guild.Id, nickArgs).ConfigureAwait(false); | ||||
args.Nickname = new API.Optional<string>(); //Remove | args.Nickname = new API.Optional<string>(); //Remove | ||||
} | } | ||||