| @@ -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 | |||
| } | |||