@@ -29,29 +29,13 @@ namespace Discord.API | |||
public TokenType AuthTokenType { get; private set; } | |||
public IRestClient RestClient { 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; | |||
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.SetHeader("accept", "*/*"); | |||
_restClient.SetHeader("authorization", authToken); | |||
_restClient.SetHeader("user-agent", DiscordConfig.UserAgent); | |||
_requestQueue = new RequestQueue(_restClient); | |||
@@ -69,6 +53,27 @@ namespace Discord.API | |||
_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 | |||
public Task Send(string method, string endpoint, GlobalBucket bucket = GlobalBucket.General) | |||
=> SendInternal(method, endpoint, null, true, bucket); | |||
@@ -122,7 +127,7 @@ namespace Discord.API | |||
stopwatch.Stop(); | |||
double milliseconds = ToMilliseconds(stopwatch); | |||
SentRequest(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||
SentRequest?.Invoke(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||
return responseStream; | |||
} | |||
@@ -134,11 +139,23 @@ namespace Discord.API | |||
stopwatch.Stop(); | |||
double milliseconds = ToMilliseconds(stopwatch); | |||
SentRequest(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||
SentRequest?.Invoke(this, new SentRequestEventArgs(method, endpoint, bytes, milliseconds)); | |||
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 | |||
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\Rest\DeleteMessagesParam.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\UploadFileParams.cs" /> | |||
<Compile Include="API\Rest\GuildPruneParams.cs" /> | |||
@@ -14,7 +14,8 @@ namespace Discord | |||
IRestClient RestClient { 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<IChannel> GetChannel(ulong id); | |||
@@ -47,43 +47,64 @@ namespace Discord.Rest | |||
_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); | |||
try | |||
{ | |||
await LoginInternal(tokenType, token).ConfigureAwait(false); | |||
await LoginInternal(email, password).ConfigureAwait(false); | |||
} | |||
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) | |||
LogoutInternal(); | |||
try | |||
{ | |||
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; } | |||
} | |||
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() | |||
{ | |||
@@ -99,9 +120,14 @@ namespace Discord.Rest | |||
{ | |||
bool wasLoggedIn = IsLoggedIn; | |||
try { _cancelTokenSource.Cancel(false); } catch { } | |||
if (_cancelTokenSource != null) | |||
{ | |||
try { _cancelTokenSource.Cancel(false); } | |||
catch { } | |||
} | |||
BaseClient = null; | |||
_currentUser = null; | |||
if (wasLoggedIn) | |||
{ | |||
@@ -87,10 +87,7 @@ namespace Discord.Rest | |||
bool isCurrentUser = (await Discord.GetCurrentUser().ConfigureAwait(false)).Id == Id; | |||
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); | |||
args.Nickname = new API.Optional<string>(); //Remove | |||
} | |||