diff --git a/PokemonGo/RocketAPI/Client.cs b/PokemonGo/RocketAPI/Client.cs
index 5d4cc5f..cf2a86b 100644
--- a/PokemonGo/RocketAPI/Client.cs
+++ b/PokemonGo/RocketAPI/Client.cs
@@ -20,7 +20,7 @@ using System.Threading;
using PokemonGo.RocketAPI.Exceptions;
using System.Text;
using System.IO;
-using DankMemes.GPSOAuthSharp;
+using Newtonsoft.Json;
#endregion
@@ -32,13 +32,14 @@ namespace PokemonGo.RocketAPI
private ISettings _settings;
private string _accessToken;
private string _apiUrl;
- private AuthType _authType = AuthType.Google;
private double _currentLat;
private double _currentLng;
private Request.Types.UnknownAuth _unknownAuth;
public static string AccessToken { get; set; } = string.Empty;
+ private readonly ILoginType login;
+
public Client(ISettings settings)
{
_settings = settings;
@@ -58,6 +59,20 @@ namespace PokemonGo.RocketAPI
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type",
"application/x-www-form-urlencoded");
+
+ login = CreateLoginType(settings);
+ }
+
+ static ILoginType CreateLoginType(ISettings settings)
+ {
+ switch (settings.AuthType)
+ {
+ case AuthType.Google:
+ return new GoogleLogin(settings.Email, settings.Password);
+ case AuthType.Ptc:
+ return new PtcLogin(settings.PtcUsername, settings.PtcPassword);
+ }
+ throw new ArgumentOutOfRangeException(nameof(settings.AuthType), "unknown auth type");
}
public async Task<CatchPokemonResponse> CatchPokemon(ulong encounterId, string spawnPointGuid, double pokemonLat,
@@ -86,36 +101,27 @@ namespace PokemonGo.RocketAPI
catchPokemonRequest);
}
- public async Task DoGoogleLogin(string email, string password)
+ public async Task Login()
{
- _authType = AuthType.Google;
- GPSOAuthClient _GPSOclient = new GPSOAuthClient(email, password);
- Dictionary<string, string> _GPSOresponse = _GPSOclient.PerformMasterLogin();
- /* string json = JsonConvert.SerializeObject(_GPSOresponse, Formatting.Indented);
- Console.WriteLine(json); */
- if (_GPSOresponse.ContainsKey("Token"))
+ string errorMessage;
+ do
{
- string token = _GPSOresponse["Token"];
- Dictionary<string, string> oauthResponse = _GPSOclient.PerformOAuth(
- token,
- "audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com",
- "com.nianticlabs.pokemongo",
- "321187995bc7cdc2b5fc91b11a96e2baa8602c62");
- /* string oauthJson = JsonConvert.SerializeObject(oauthResponse, Formatting.Indented);
- Console.WriteLine(oauthJson); */
- _accessToken = oauthResponse["Auth"];
- }
- }
+ errorMessage = null;
+
+ try
+ {
+ _accessToken = await login.GetAccessToken().ConfigureAwait(false);
+ }
+ catch (LoginFailedException) { errorMessage = "Login failed - wrong username or password? - Restarting"; }
+ catch (PtcOfflineException) { errorMessage = "PTC login server is down - Restarting"; }
+ catch (JsonReaderException) { errorMessage = "Json Reader Exception - Server down? - Restarting"; }
+ catch (Exception ex) { errorMessage = ex.ToString() + "Exception - Please report - Restarting"; }
+
+ if (errorMessage != null)
+ ColoredConsoleWrite(ConsoleColor.White, errorMessage);
+
+ } while (errorMessage != null);
- public async Task DoPtcLogin(string username, string password)
- {
- try
- {
- _accessToken = await PtcLogin.GetAccessToken(username, password);
- _authType = AuthType.Ptc;
- }
- catch (Newtonsoft.Json.JsonReaderException) { ColoredConsoleWrite(ConsoleColor.White, "Json Reader Exception - Server down? - Restarting"); DoPtcLogin(username, password); }
- catch (Exception ex) { ColoredConsoleWrite(ConsoleColor.White, ex.ToString() + "Exception - Please report - Restarting"); DoPtcLogin(username, password); }
}
public async Task<EncounterResponse> EncounterPokemon(ulong encounterId, string spawnPointGuid)
@@ -326,7 +332,7 @@ namespace PokemonGo.RocketAPI
public async Task<GetPlayerResponse> GetProfile()
{
- var profileRequest = RequestBuilder.GetInitialRequest(_accessToken, _authType, _currentLat, _currentLng, 10,
+ var profileRequest = RequestBuilder.GetInitialRequest(_accessToken, _settings.AuthType, _currentLat, _currentLng, 10,
new Request.Types.Requests { Type = (int)RequestType.GET_PLAYER });
return
await _httpClient.PostProtoPayload<Request, GetPlayerResponse>($"https://{_apiUrl}/rpc", profileRequest);
@@ -383,7 +389,7 @@ namespace PokemonGo.RocketAPI
public async Task SetServer()
{
- var serverRequest = RequestBuilder.GetInitialRequest(_accessToken, _authType, _currentLat, _currentLng, 10,
+ var serverRequest = RequestBuilder.GetInitialRequest(_accessToken, _settings.AuthType, _currentLat, _currentLng, 10,
RequestType.GET_PLAYER, RequestType.GET_HATCHED_OBJECTS, RequestType.GET_INVENTORY,
RequestType.CHECK_AWARDED_BADGES, RequestType.DOWNLOAD_SETTINGS);
var serverResponse = await _httpClient.PostProto(Resources.RpcUrl, serverRequest);
diff --git a/PokemonGo/RocketAPI/Exceptions/LoginFailedException.cs b/PokemonGo/RocketAPI/Exceptions/LoginFailedException.cs
new file mode 100644
index 0000000..37bd893
--- /dev/null
+++ b/PokemonGo/RocketAPI/Exceptions/LoginFailedException.cs
@@ -0,0 +1,8 @@
+using System;
+
+namespace PokemonGo.RocketAPI.Exceptions
+{
+ public class LoginFailedException : Exception
+ {
+ }
+}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/GPSOAuthSharp.cs b/PokemonGo/RocketAPI/GPSOAuthSharp.cs
deleted file mode 100644
index 9415c32..0000000
--- a/PokemonGo/RocketAPI/GPSOAuthSharp.cs
+++ /dev/null
@@ -1,177 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.IO;
-using System.Linq;
-using System.Net;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace DankMemes.GPSOAuthSharp
-{
- // gpsoauth:__init__.py
- // URL: https://github.com/simon-weber/gpsoauth/blob/master/gpsoauth/__init__.py
- public class GPSOAuthClient
- {
- static string b64Key = "AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3" +
- "iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pK" +
- "RI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/" +
- "6rmf5AAAAAwEAAQ==";
- static RSAParameters androidKey = GoogleKeyUtils.KeyFromB64(b64Key);
-
- static string version = "0.0.5";
- static string authUrl = "https://android.clients.google.com/auth";
- static string userAgent = "GPSOAuthSharp/" + version;
-
- private string email;
- private string password;
-
- public GPSOAuthClient(string email, string password)
- {
- this.email = email;
- this.password = password;
- }
-
- // _perform_auth_request
- private Dictionary<string, string> PerformAuthRequest(Dictionary<string, string> data)
- {
- NameValueCollection nvc = new NameValueCollection();
- foreach (var kvp in data)
- {
- nvc.Add(kvp.Key.ToString(), kvp.Value.ToString());
- }
- using (WebClient client = new WebClient())
- {
- client.Headers.Add(HttpRequestHeader.UserAgent, userAgent);
- string result;
- try
- {
- byte[] response = client.UploadValues(authUrl, nvc);
- result = Encoding.UTF8.GetString(response);
- }
- catch (WebException e)
- {
- result = new StreamReader(e.Response.GetResponseStream()).ReadToEnd();
- }
- return GoogleKeyUtils.ParseAuthResponse(result);
- }
- }
-
- // perform_master_login
- public Dictionary<string, string> PerformMasterLogin(string service = "ac2dm",
- string deviceCountry = "us", string operatorCountry = "us", string lang = "en", int sdkVersion = 21)
- {
- string signature = GoogleKeyUtils.CreateSignature(email, password, androidKey);
- var dict = new Dictionary<string, string> {
- { "accountType", "HOSTED_OR_GOOGLE" },
- { "Email", email },
- { "has_permission", 1.ToString() },
- { "add_account", 1.ToString() },
- { "EncryptedPasswd", signature},
- { "service", service },
- { "source", "android" },
- { "device_country", deviceCountry },
- { "operatorCountry", operatorCountry },
- { "lang", lang },
- { "sdk_version", sdkVersion.ToString() }
- };
- return PerformAuthRequest(dict);
- }
-
- // perform_oauth
- public Dictionary<string, string> PerformOAuth(string masterToken, string service, string app, string clientSig,
- string deviceCountry = "us", string operatorCountry = "us", string lang = "en", int sdkVersion = 21)
- {
- var dict = new Dictionary<string, string> {
- { "accountType", "HOSTED_OR_GOOGLE" },
- { "Email", email },
- { "has_permission", 1.ToString() },
- { "EncryptedPasswd", masterToken},
- { "service", service },
- { "source", "android" },
- { "app", app },
- { "client_sig", clientSig },
- { "device_country", deviceCountry },
- { "operatorCountry", operatorCountry },
- { "lang", lang },
- { "sdk_version", sdkVersion.ToString() }
- };
- return PerformAuthRequest(dict);
- }
- }
-
- // gpsoauth:google.py
- // URL: https://github.com/simon-weber/gpsoauth/blob/master/gpsoauth/google.py
- class GoogleKeyUtils
- {
- // key_from_b64
- // BitConverter has different endianness, hence the Reverse()
- public static RSAParameters KeyFromB64(string b64Key)
- {
- byte[] decoded = Convert.FromBase64String(b64Key);
- int modLength = BitConverter.ToInt32(decoded.Take(4).Reverse().ToArray(), 0);
- byte[] mod = decoded.Skip(4).Take(modLength).ToArray();
- int expLength = BitConverter.ToInt32(decoded.Skip(modLength + 4).Take(4).Reverse().ToArray(), 0);
- byte[] exponent = decoded.Skip(modLength + 8).Take(expLength).ToArray();
- RSAParameters rsaKeyInfo = new RSAParameters();
- rsaKeyInfo.Modulus = mod;
- rsaKeyInfo.Exponent = exponent;
- return rsaKeyInfo;
- }
-
- // key_to_struct
- // Python version returns a string, but we use byte[] to get the same results
- public static byte[] KeyToStruct(RSAParameters key)
- {
- byte[] modLength = { 0x00, 0x00, 0x00, 0x80 };
- byte[] mod = key.Modulus;
- byte[] expLength = { 0x00, 0x00, 0x00, 0x03 };
- byte[] exponent = key.Exponent;
- return DataTypeUtils.CombineBytes(modLength, mod, expLength, exponent);
- }
-
- // parse_auth_response
- public static Dictionary<string, string> ParseAuthResponse(string text)
- {
- Dictionary<string, string> responseData = new Dictionary<string, string>();
- foreach (string line in text.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
- {
- string[] parts = line.Split('=');
- responseData.Add(parts[0], parts[1]);
- }
- return responseData;
- }
-
- // signature
- public static string CreateSignature(string email, string password, RSAParameters key)
- {
- RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
- rsa.ImportParameters(key);
- SHA1 sha1 = SHA1.Create();
- byte[] prefix = { 0x00 };
- byte[] hash = sha1.ComputeHash(GoogleKeyUtils.KeyToStruct(key)).Take(4).ToArray();
- byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(email + "\x00" + password), true);
- return DataTypeUtils.UrlSafeBase64(DataTypeUtils.CombineBytes(prefix, hash, encrypted));
- }
- }
-
- class DataTypeUtils
- {
- public static string UrlSafeBase64(byte[] byteArray)
- {
- return Convert.ToBase64String(byteArray).Replace('+', '-').Replace('/', '_');
- }
-
- public static byte[] CombineBytes(params byte[][] arrays)
- {
- byte[] rv = new byte[arrays.Sum(a => a.Length)];
- int offset = 0;
- foreach (byte[] array in arrays)
- {
- Buffer.BlockCopy(array, 0, rv, offset, array.Length);
- offset += array.Length;
- }
- return rv;
- }
- }
-}
diff --git a/PokemonGo/RocketAPI/Helpers/JsonHelper.cs b/PokemonGo/RocketAPI/Helpers/JsonHelper.cs
deleted file mode 100644
index 2dd2fcd..0000000
--- a/PokemonGo/RocketAPI/Helpers/JsonHelper.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-#region
-
-using Newtonsoft.Json.Linq;
-
-#endregion
-
-namespace PokemonGo.RocketAPI.Helpers
-{
- public class JsonHelper
- {
- public static string GetValue(string json, string key)
- {
- var jObject = JObject.Parse(json);
- return jObject[key].ToString();
- }
- }
-}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/Login/GoogleLogin.cs b/PokemonGo/RocketAPI/Login/GoogleLogin.cs
index c91f726..10c9c70 100644
--- a/PokemonGo/RocketAPI/Login/GoogleLogin.cs
+++ b/PokemonGo/RocketAPI/Login/GoogleLogin.cs
@@ -1,115 +1,180 @@
-#region
-
+using PokemonGo.RocketAPI.Exceptions;
using System;
using System.Collections.Generic;
-using System.Diagnostics;
-using System.Threading;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Text;
using System.Threading.Tasks;
-using System.Windows.Forms;
-using PokemonGo.RocketAPI.Helpers;
-using System.IO;
-
-
-#endregion
namespace PokemonGo.RocketAPI.Login
{
- public static class GoogleLogin
+ public class GoogleLogin : ILoginType
{
- private const string OauthTokenEndpoint = "https://www.googleapis.com/oauth2/v4/token";
- private const string OauthEndpoint = "https://accounts.google.com/o/oauth2/device/code";
- private const string ClientId = "848232511240-73ri3t7plvk96pj4f85uj8otdat2alem.apps.googleusercontent.com";
- private const string ClientSecret = "NCjF1TLi2CcY6t5mt0ZveuL7";
-
- /// <summary>
- /// Gets the access token from Google
- /// </summary>
- /// <param name="deviceCode"></param>
- /// <returns>tokenResponse</returns>
- public static async Task<TokenResponseModel> GetAccessToken(DeviceCodeModel deviceCode)
- {
- //Poll until user submitted code..
- TokenResponseModel tokenResponse;
- do
- {
- await Task.Delay(2000);
- tokenResponse = await PollSubmittedToken(deviceCode.device_code);
- } while (tokenResponse.access_token == null || tokenResponse.refresh_token == null);
+ private readonly string password;
+ private readonly string email;
- return tokenResponse;
+ public GoogleLogin(string email, string password)
+ {
+ this.email = email;
+ this.password = password;
}
- public static async Task<TokenResponseModel> GetAccessToken(string refreshToken)
+ public async Task<string> GetAccessToken()
{
- return await HttpClientHelper.PostFormEncodedAsync<TokenResponseModel>(OauthTokenEndpoint,
- new KeyValuePair<string, string>("access_type", "offline"),
- new KeyValuePair<string, string>("client_id", ClientId),
- new KeyValuePair<string, string>("client_secret", ClientSecret),
- new KeyValuePair<string, string>("refresh_token", refreshToken),
- new KeyValuePair<string, string>("grant_type", "refresh_token"),
- new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+ var _GPSOresponse = await PerformMasterLoginAsync(email, password).ConfigureAwait(false);
+ string token;
+ if (!_GPSOresponse.TryGetValue("Token", out token))
+ throw new LoginFailedException();
+
+ var oauthResponse = await PerformOAuthAsync(
+ token,
+ "audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com",
+ "com.nianticlabs.pokemongo",
+ "321187995bc7cdc2b5fc91b11a96e2baa8602c62",
+ email).ConfigureAwait(false);
+ return oauthResponse["Auth"];
}
- public static async Task<DeviceCodeModel> GetDeviceCode()
- {
- var deviceCode = await HttpClientHelper.PostFormEncodedAsync<DeviceCodeModel>(OauthEndpoint,
- new KeyValuePair<string, string>("client_id", ClientId),
- new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+ private static string b64Key = "AAAAgMom/1a/v0lblO2Ubrt60J2gcuXSljGFQXgcyZWveWLEwo6prwgi3" +
+ "iJIZdodyhKZQrNWp5nKJ3srRXcUW+F1BD3baEVGcmEgqaLZUNBjm057pK" +
+ "RI16kB0YppeGx5qIQ5QjKzsR8ETQbKLNWgRY0QRNVz34kMJR3P/LgHax/" +
+ "6rmf5AAAAAwEAAQ==";
+
+ private static RSAParameters androidKey = GoogleKeyUtils.KeyFromB64(b64Key);
- try
+ private static string version = "0.0.5";
+ private static string authUrl = "https://android.clients.google.com/auth";
+ private static string userAgent = "GPSOAuthSharp/" + version;
+
+ private async static Task<IDictionary<string, string>> PerformAuthRequestAsync(IDictionary<string, string> data)
+ {
+ var handler = new HttpClientHandler
{
- //ColoredConsoleWrite("Google Device Code copied to clipboard");
- System.Console.WriteLine($"Goto: http://www.google.com/device & enter {deviceCode.user_code}");
- File.AppendAllText(AppDomain.CurrentDomain.BaseDirectory + @"\Logs.txt", "[" + DateTime.Now.ToString("HH:mm:ss tt") + $"] Goto: http://www.google.com/device & enter {deviceCode.user_code}");
- Process.Start(@"http://www.google.com/device");
- var thread = new Thread(() => Clipboard.SetText(deviceCode.user_code)); //Copy device code
- thread.SetApartmentState(ApartmentState.STA); //Set the thread to STA
- thread.Start();
- thread.Join();
- }
- catch (Exception)
+ AutomaticDecompression = DecompressionMethods.GZip,
+ AllowAutoRedirect = false
+ };
+ using (var tempHttpClient = new HttpClient(handler))
{
- //System.Console.WriteLine("Couldnt copy to clipboard, do it manually");
- //System.Console.WriteLine($"Goto: http://www.google.com/device & enter {deviceCode.user_code}");
+ tempHttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
+
+ HttpResponseMessage response;
+ using (var formUrlEncodedContent = new FormUrlEncodedContent(data))
+ {
+ response = await tempHttpClient.PostAsync(authUrl, formUrlEncodedContent).ConfigureAwait(false);
+ }
+
+ var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return GoogleKeyUtils.ParseAuthResponse(content);
}
+ }
- return deviceCode;
+ private static IDictionary<string, string> GenerateBaseRequest(string email, string encryptedPassword, string service)
+ => new Dictionary<string, string> {
+ { "accountType", "HOSTED_OR_GOOGLE" },
+ { "Email", email },
+ { "has_permission", "1" },
+ { "EncryptedPasswd", encryptedPassword},
+ { "service", service },
+ { "source", "android" },
+ { "device_country", "us" },
+ { "operatorCountry", "us" },
+ { "lang", "en" },
+ { "sdk_version", "21" }
+ };
+
+ private static Task<IDictionary<string, string>> PerformMasterLoginAsync(string email, string password)
+ {
+ var signature = GoogleKeyUtils.CreateSignature(email, password, androidKey);
+ var request = GenerateBaseRequest(email, signature, "ac2dm");
+ request.Add("add_account", "1");
+ return PerformAuthRequestAsync(request);
}
- private static async Task<TokenResponseModel> PollSubmittedToken(string deviceCode)
+ private static Task<IDictionary<string, string>> PerformOAuthAsync(string masterToken, string service, string app, string clientSig, string email)
{
- return await HttpClientHelper.PostFormEncodedAsync<TokenResponseModel>(OauthTokenEndpoint,
- new KeyValuePair<string, string>("client_id", ClientId),
- new KeyValuePair<string, string>("client_secret", ClientSecret),
- new KeyValuePair<string, string>("code", deviceCode),
- new KeyValuePair<string, string>("grant_type", "http://oauth.net/grant_type/device/1.0"),
- new KeyValuePair<string, string>("scope", "openid email https://www.googleapis.com/auth/userinfo.email"));
+ var request = GenerateBaseRequest(email, masterToken, service);
+ request.Add("app", app);
+ request.Add("client_sig", clientSig);
+ return PerformAuthRequestAsync(request);
}
+ }
+ internal class GoogleKeyUtils
+ {
+ // key_from_b64
+ // BitConverter has different endianness, hence the Reverse()
+ public static RSAParameters KeyFromB64(string b64Key)
+ {
+ var decoded = Convert.FromBase64String(b64Key);
+ var modLength = BitConverter.ToInt32(decoded.Take(4).Reverse().ToArray(), 0);
+ var mod = decoded.Skip(4).Take(modLength).ToArray();
+ var expLength = BitConverter.ToInt32(decoded.Skip(modLength + 4).Take(4).Reverse().ToArray(), 0);
+ var exponent = decoded.Skip(modLength + 8).Take(expLength).ToArray();
+ var rsaKeyInfo = new RSAParameters
+ {
+ Modulus = mod,
+ Exponent = exponent
+ };
+ return rsaKeyInfo;
+ }
- internal class ErrorResponseModel
+ // key_to_struct
+ // Python version returns a string, but we use byte[] to get the same results
+ public static byte[] KeyToStruct(RSAParameters key)
{
- public string error { get; set; }
- public string error_description { get; set; }
+ byte[] modLength = { 0x00, 0x00, 0x00, 0x80 };
+ var mod = key.Modulus;
+ byte[] expLength = { 0x00, 0x00, 0x00, 0x03 };
+ var exponent = key.Exponent;
+ return DataTypeUtils.CombineBytes(modLength, mod, expLength, exponent);
}
- public class TokenResponseModel
+ // parse_auth_response
+ public static Dictionary<string, string> ParseAuthResponse(string text)
{
- public string access_token { get; set; }
- public string token_type { get; set; }
- public int expires_in { get; set; }
- public string refresh_token { get; set; }
- public string id_token { get; set; }
+ var responseData = new Dictionary<string, string>();
+ foreach (string line in text.Split(new string[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ var parts = line.Split('=');
+ responseData.Add(parts[0], parts[1]);
+ }
+ return responseData;
}
+ // signature
+ public static string CreateSignature(string email, string password, RSAParameters key)
+ {
+ using (var rsa = new RSACryptoServiceProvider())
+ {
+ rsa.ImportParameters(key);
+ var sha1 = SHA1.Create();
+ byte[] prefix = { 0x00 };
+ var hash = sha1.ComputeHash(GoogleKeyUtils.KeyToStruct(key)).Take(4).ToArray();
+ var encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(email + "\x00" + password), true);
+ return DataTypeUtils.UrlSafeBase64(DataTypeUtils.CombineBytes(prefix, hash, encrypted));
+ }
+ }
- public class DeviceCodeModel
+ private class DataTypeUtils
{
- public string verification_url { get; set; }
- public int expires_in { get; set; }
- public int interval { get; set; }
- public string device_code { get; set; }
- public string user_code { get; set; }
+ public static string UrlSafeBase64(byte[] byteArray)
+ {
+ return Convert.ToBase64String(byteArray).Replace('+', '-').Replace('/', '_');
+ }
+
+ public static byte[] CombineBytes(params byte[][] arrays)
+ {
+ var rv = new byte[arrays.Sum(a => a.Length)];
+ var offset = 0;
+ foreach (byte[] array in arrays)
+ {
+ Buffer.BlockCopy(array, 0, rv, offset, array.Length);
+ offset += array.Length;
+ }
+ return rv;
+ }
}
}
}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/Login/ILoginType.cs b/PokemonGo/RocketAPI/Login/ILoginType.cs
new file mode 100644
index 0000000..0c102a0
--- /dev/null
+++ b/PokemonGo/RocketAPI/Login/ILoginType.cs
@@ -0,0 +1,16 @@
+using System.Threading.Tasks;
+
+namespace PokemonGo.RocketAPI.Login
+{
+ /// <summary>
+ /// Interface for the login into the game using either Google or PTC
+ /// </summary>
+ interface ILoginType
+ {
+ /// <summary>
+ /// Gets the access token.
+ /// </summary>
+ /// <returns></returns>
+ Task<string> GetAccessToken();
+ }
+}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/Login/PtcLogin.cs b/PokemonGo/RocketAPI/Login/PtcLogin.cs
index 7a3c900..0cf6b04 100644
--- a/PokemonGo/RocketAPI/Login/PtcLogin.cs
+++ b/PokemonGo/RocketAPI/Login/PtcLogin.cs
@@ -1,20 +1,24 @@
-#region
-
+using Newtonsoft.Json;
+using PokemonGo.RocketAPI.Exceptions;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;
-using PokemonGo.RocketAPI.Exceptions;
-using PokemonGo.RocketAPI.Helpers;
-
-#endregion
namespace PokemonGo.RocketAPI.Login
{
- internal static class PtcLogin
+ class PtcLogin : ILoginType
{
- public static async Task<string> GetAccessToken(string username, string password)
+ readonly string password;
+ readonly string username;
+
+ public PtcLogin(string username, string password)
+ {
+ this.username = username;
+ this.password = password;
+ }
+ public async Task<string> GetAccessToken()
{
var handler = new HttpClientHandler
{
@@ -25,44 +29,88 @@ namespace PokemonGo.RocketAPI.Login
using (var tempHttpClient = new HttpClient(handler))
{
//Get session cookie
- var sessionResp = await tempHttpClient.GetAsync(Resources.PtcLoginUrl);
- var data = await sessionResp.Content.ReadAsStringAsync();
- var lt = JsonHelper.GetValue(data, "lt");
- var executionId = JsonHelper.GetValue(data, "execution");
+ var sessionData = await GetSessionCookie(tempHttpClient).ConfigureAwait(false);
//Login
- var loginResp = await tempHttpClient.PostAsync(Resources.PtcLoginUrl,
- new FormUrlEncodedContent(
- new[]
- {
- new KeyValuePair<string, string>("lt", lt),
- new KeyValuePair<string, string>("execution", executionId),
- new KeyValuePair<string, string>("_eventId", "submit"),
- new KeyValuePair<string, string>("username", username),
- new KeyValuePair<string, string>("password", password)
- }));
-
- var ticketId = HttpUtility.ParseQueryString(loginResp.Headers.Location.Query)["ticket"];
- if (ticketId == null)
- throw new PtcOfflineException();
-
- //Get tokenvar
- var tokenResp = await tempHttpClient.PostAsync(Resources.PtcLoginOauth,
- new FormUrlEncodedContent(
- new[]
- {
- new KeyValuePair<string, string>("client_id", "mobile-app_pokemon-go"),
- new KeyValuePair<string, string>("redirect_uri",
- "https://www.nianticlabs.com/pokemongo/error"),
- new KeyValuePair<string, string>("client_secret",
- "w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR"),
- new KeyValuePair<string, string>("grant_type", "refresh_token"),
- new KeyValuePair<string, string>("code", ticketId)
- }));
-
- var tokenData = await tokenResp.Content.ReadAsStringAsync();
- return HttpUtility.ParseQueryString(tokenData)["access_token"];
+ var ticketId = await GetLoginTicket(username, password, tempHttpClient, sessionData).ConfigureAwait(false);
+
+ //Get tokenvar
+ return await GetToken(tempHttpClient, ticketId).ConfigureAwait(false);
+ }
+ }
+
+ private static string ExtracktTicketFromResponse(HttpResponseMessage loginResp)
+ {
+ var location = loginResp.Headers.Location;
+ if (location == null)
+ throw new LoginFailedException();
+
+ var ticketId = HttpUtility.ParseQueryString(location.Query)["ticket"];
+
+ if (ticketId == null)
+ throw new PtcOfflineException();
+
+ return ticketId;
+ }
+
+ private static IDictionary<string, string> GenerateLoginRequest(SessionData sessionData, string user, string pass)
+ => new Dictionary<string, string>
+ {
+ { "lt", sessionData.Lt },
+ { "execution", sessionData.Execution },
+ { "_eventId", "submit" },
+ { "username", user },
+ { "password", pass }
+ };
+
+ private static IDictionary<string, string> GenerateTokenVarRequest(string ticketId)
+ => new Dictionary<string, string>
+ {
+ {"client_id", "mobile-app_pokemon-go"},
+ {"redirect_uri", "https://www.nianticlabs.com/pokemongo/error"},
+ {"client_secret", "w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR"},
+ {"grant_type", "refresh_token"},
+ {"code", ticketId}
+ };
+
+ private static async Task<string> GetLoginTicket(string username, string password, HttpClient tempHttpClient, SessionData sessionData)
+ {
+ HttpResponseMessage loginResp;
+ var loginRequest = GenerateLoginRequest(sessionData, username, password);
+ using (var formUrlEncodedContent = new FormUrlEncodedContent(loginRequest))
+ {
+ loginResp = await tempHttpClient.PostAsync(Resources.PtcLoginUrl, formUrlEncodedContent).ConfigureAwait(false);
}
+
+ var ticketId = ExtracktTicketFromResponse(loginResp);
+ return ticketId;
+ }
+
+ private static async Task<SessionData> GetSessionCookie(HttpClient tempHttpClient)
+ {
+ var sessionResp = await tempHttpClient.GetAsync(Resources.PtcLoginUrl).ConfigureAwait(false);
+ var data = await sessionResp.Content.ReadAsStringAsync().ConfigureAwait(false);
+ var sessionData = JsonConvert.DeserializeObject<SessionData>(data);
+ return sessionData;
+ }
+
+ private static async Task<string> GetToken(HttpClient tempHttpClient, string ticketId)
+ {
+ HttpResponseMessage tokenResp;
+ var tokenRequest = GenerateTokenVarRequest(ticketId);
+ using (var formUrlEncodedContent = new FormUrlEncodedContent(tokenRequest))
+ {
+ tokenResp = await tempHttpClient.PostAsync(Resources.PtcLoginOauth, formUrlEncodedContent).ConfigureAwait(false);
+ }
+
+ var tokenData = await tokenResp.Content.ReadAsStringAsync().ConfigureAwait(false);
+ return HttpUtility.ParseQueryString(tokenData)["access_token"];
+ }
+
+ private class SessionData
+ {
+ public string Lt { get; set; }
+ public string Execution { get; set; }
}
}
-}
+}
\ No newline at end of file
diff --git a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
index 71e4fbd..8760094 100644
--- a/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
+++ b/PokemonGo/RocketAPI/PokemonGo.RocketAPI.csproj
@@ -71,21 +71,21 @@
<Compile Include="Enums\AuthType.cs" />
<Compile Include="Enums\MiscEnums.cs" />
<Compile Include="Enums\RequestType.cs" />
+ <Compile Include="Exceptions\LoginFailedException.cs" />
<Compile Include="Exceptions\PtcOfflineException.cs" />
<Compile Include="Extensions\DateTimeExtensions.cs" />
<Compile Include="GeneratedCode\AllEnum.cs" />
<Compile Include="GeneratedCode\Payloads.cs" />
<Compile Include="GeneratedCode\Request.cs" />
<Compile Include="GeneratedCode\Response.cs" />
- <Compile Include="GPSOAuthSharp.cs" />
<Compile Include="Helpers\HttpClientHelper.cs" />
- <Compile Include="Helpers\JsonHelper.cs" />
<Compile Include="Helpers\ProtoHelper.cs" />
<Compile Include="Helpers\RetryHandler.cs" />
<Compile Include="Helpers\S2Helper.cs" />
<Compile Include="Helpers\Utils.cs" />
<Compile Include="ISettings.cs" />
<Compile Include="Login\GoogleLogin.cs" />
+ <Compile Include="Login\ILoginType.cs" />
<Compile Include="Login\PtcLogin.cs" />
<None Include="app.config" />
<Compile Include="Client.cs" />
diff --git a/PokemonGo/RocketAPI/Window/MainForm.cs b/PokemonGo/RocketAPI/Window/MainForm.cs
index 2b255f5..88454b7 100644
--- a/PokemonGo/RocketAPI/Window/MainForm.cs
+++ b/PokemonGo/RocketAPI/Window/MainForm.cs
@@ -261,17 +261,16 @@ namespace PokemonGo.RocketAPI.Window
{
switch (ClientSettings.AuthType)
{
+
case AuthType.Ptc:
ColoredConsoleWrite(Color.Green, "Login Type: Pokemon Trainers Club");
- await client.DoPtcLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
break;
case AuthType.Google:
ColoredConsoleWrite(Color.Green, "Login Type: Google");
- await client.DoGoogleLogin(ClientSettings.Email, ClientSettings.Password);
-
break;
}
+ await client.Login();
await client.SetServer();
var profile = await client.GetProfile();
var settings = await client.GetSettings();
@@ -284,9 +283,9 @@ namespace PokemonGo.RocketAPI.Window
ConsoleLevelTitle(profile.Profile.Username, client);
// Write the players ingame details
- InitializeMap();
ColoredConsoleWrite(Color.Yellow, "----------------------------");
- if (ClientSettings.AuthType == AuthType.Ptc)
+ /*// dont actually want to display info but keeping here incase people want to \O_O/
+ * if (ClientSettings.AuthType == AuthType.Ptc)
{
ColoredConsoleWrite(Color.Cyan, "Account: " + ClientSettings.PtcUsername);
ColoredConsoleWrite(Color.Cyan, "Password: " + ClientSettings.PtcPassword + "\n");
@@ -295,7 +294,7 @@ namespace PokemonGo.RocketAPI.Window
{
ColoredConsoleWrite(Color.Cyan, "Email: " + ClientSettings.Email);
ColoredConsoleWrite(Color.Cyan, "Password: " + ClientSettings.Password + "\n");
- }
+ }*/
ColoredConsoleWrite(Color.DarkGray, "Name: " + profile.Profile.Username);
ColoredConsoleWrite(Color.DarkGray, "Team: " + profile.Profile.Team);
if (profile.Profile.Currency.ToArray()[0].Amount > 0) // If player has any pokecoins it will show how many they have.
@@ -313,6 +312,7 @@ namespace PokemonGo.RocketAPI.Window
ColoredConsoleWrite(Color.DarkGray, "Unable to get Country/Place");
}
+
ColoredConsoleWrite(Color.Yellow, "----------------------------");
// I believe a switch is more efficient and easier to read.
@@ -383,11 +383,13 @@ namespace PokemonGo.RocketAPI.Window
Execute();
}
}
+
}
private static string CallAPI(string elem, double lat, double lon)
{
- using (XmlReader reader = XmlReader.Create(@"http://api.geonames.org/findNearby?lat=" + lat + "&lng=" + lon + "&username=pokemonbot"))
+
+ using (XmlReader reader = XmlReader.Create(@"http://api.geonames.org/findNearby?lat=" + lat + "&lng=" + lon + "&username=pokemongobot"))
{
while (reader.Read())
{
@@ -417,6 +419,7 @@ namespace PokemonGo.RocketAPI.Window
}
return "Error";
}
+
private async Task ExecuteCatchAllNearbyPokemons(Client client)
{
var mapObjects = await client.GetMapObjects();
@@ -944,11 +947,33 @@ namespace PokemonGo.RocketAPI.Window
var inventory = await client.GetInventory();
var stats = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.PlayerStats).ToArray();
var profile = await client.GetProfile();
+ Int16 hoursLeft = 0; Int16 minutesLeft = 0; Int32 secondsLeft = 0; double xpSec = 0;
foreach (var v in stats)
if (v != null)
{
int XpDiff = GetXpDiff(client, v.Level);
- SetStatusText(string.Format(Username + " | Level: {0:0} - ({2:0} / {3:0}) | Runtime {1} | Stardust: {4:0}", v.Level, _getSessionRuntimeInTimeFormat(), (v.Experience - v.PrevLevelXp - XpDiff), (v.NextLevelXp - v.PrevLevelXp - XpDiff), profile.Profile.Currency.ToArray()[1].Amount) + " | XP/Hour: " + Math.Round(TotalExperience / GetRuntime()) + " | Pokemon/Hour: " + Math.Round(TotalPokemon / GetRuntime()));
+ //Calculating the exp needed to level up
+ Single expNextLvl = (v.NextLevelXp - v.Experience);
+ //Calculating the exp made per second
+ xpSec = (Math.Round(TotalExperience / GetRuntime()) / 60) / 60;
+ //Calculating the seconds left to level up
+ if (xpSec != 0)
+ secondsLeft = Convert.ToInt32((expNextLvl / xpSec));
+ //formatting data to make an output like DateFormat
+ while (secondsLeft > 60)
+ {
+ secondsLeft -= 60;
+ if (minutesLeft < 60)
+ {
+ minutesLeft++;
+ }
+ else
+ {
+ minutesLeft = 0;
+ hoursLeft++;
+ }
+ }
+ SetStatusText(string.Format(Username + " | Level: {0:0} - ({2:0} / {3:0}) | Runtime {1} | Stardust: {4:0}", v.Level, _getSessionRuntimeInTimeFormat(), (v.Experience - v.PrevLevelXp - XpDiff), (v.NextLevelXp - v.PrevLevelXp - XpDiff), profile.Profile.Currency.ToArray()[1].Amount) + " | XP/Hour: " + Math.Round(TotalExperience / GetRuntime()) + " | Pokemon/Hour: " + Math.Round(TotalPokemon / GetRuntime()) + " | NextLevel in: " + hoursLeft + ":" + minutesLeft + ":" + secondsLeft);
}
await Task.Delay(1000);
ConsoleLevelTitle(Username, client);
@@ -1042,6 +1067,15 @@ namespace PokemonGo.RocketAPI.Window
return 0;
}
+ public void confirmBotStopped()
+ {
+ //ConsoleClear(); // dont really want the console to be wipped on bot stop, unnecessary
+ ColoredConsoleWrite(Color.Red, $"Bot successfully stopped.");
+ startStopBotToolStripMenuItem.Text = "Start Bot";
+ Stopping = false;
+ bot_started = false;
+ }
+
private void logTextBox_TextChanged(object sender, EventArgs e)
{
logTextBox.SelectionStart = logTextBox.Text.Length;
@@ -1199,20 +1233,20 @@ namespace PokemonGo.RocketAPI.Window
{
objectListView1.ButtonClick += PokemonListButton_Click;
- /* pkmnName.ImageGetter = delegate (object rowObject)
- {
- PokemonData pokemon = (PokemonData)rowObject;
+ /* pkmnName.ImageGetter = delegate (object rowObject)
+ {
+ PokemonData pokemon = (PokemonData)rowObject;
- String key = pokemon.PokemonId.ToString();
+ String key = pokemon.PokemonId.ToString();
- if (!objectListView1.SmallImageList.Images.ContainsKey(key))
- {
- Image largeImage = GetPokemonImage((int)pokemon.PokemonId);
- objectListView1.SmallImageList.Images.Add(key, largeImage);
- objectListView1.LargeImageList.Images.Add(key, largeImage);
- }
- return key;
- }; */
+ if (!objectListView1.SmallImageList.Images.ContainsKey(key))
+ {
+ Image largeImage = GetPokemonImage((int)pokemon.PokemonId);
+ objectListView1.SmallImageList.Images.Add(key, largeImage);
+ objectListView1.LargeImageList.Images.Add(key, largeImage);
+ }
+ return key;
+ }; */
objectListView1.CellToolTipShowing += delegate (object sender, ToolTipShowingEventArgs args)
{
@@ -1248,18 +1282,10 @@ namespace PokemonGo.RocketAPI.Window
objectListView1.Enabled = false;
client2 = new Client(ClientSettings);
-
try
{
- switch (ClientSettings.AuthType)
- {
- case AuthType.Ptc:
- await client2.DoPtcLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
- break;
- case AuthType.Google:
- await client2.DoGoogleLogin(ClientSettings.Email, ClientSettings.Password);
- break;
- }
+
+ await client2.Login();
await client2.SetServer();
var inventory = await client2.GetInventory();
var pokemons = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon).Where(p => p != null && p?.PokemonId > 0).OrderByDescending(key => key.Cp);
diff --git a/PokemonGo/RocketAPI/Window/PokeUi.cs b/PokemonGo/RocketAPI/Window/PokeUi.cs
index 79cc1e1..5518444 100644
--- a/PokemonGo/RocketAPI/Window/PokeUi.cs
+++ b/PokemonGo/RocketAPI/Window/PokeUi.cs
@@ -38,15 +38,9 @@ namespace PokemonGo.RocketAPI.Window
try
{
- switch (ClientSettings.AuthType)
- {
- case AuthType.Ptc:
- await client.DoPtcLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
- break;
- case AuthType.Google:
- await client.DoGoogleLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
- break;
- }
+
+ await client.Login();
+
await client.SetServer();
var profile = await client.GetProfile();
var inventory = await client.GetInventory();
diff --git a/PokemonGo/RocketAPI/Window/PokemonForm.cs b/PokemonGo/RocketAPI/Window/PokemonForm.cs
index 5411bd3..c558040 100644
--- a/PokemonGo/RocketAPI/Window/PokemonForm.cs
+++ b/PokemonGo/RocketAPI/Window/PokemonForm.cs
@@ -32,22 +32,15 @@ namespace PokemonGo.RocketAPI.Window
try
{
- switch (ClientSettings.AuthType)
- {
- case AuthType.Ptc:
- await client.DoPtcLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
- break;
- case AuthType.Google:
- await client.DoGoogleLogin(ClientSettings.PtcUsername, ClientSettings.PtcPassword);
- break;
- }
+
+ await client.Login();
await client.SetServer();
var inventory = await client.GetInventory();
var pokemons =
inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Pokemon)
.Where(p => p != null && p?.PokemonId > 0).OrderByDescending(key => key.Cp);
-
+
foreach (var pokemon in pokemons)
{
diff --git a/PokemonGo/RocketAPI/Window/Properties/AssemblyInfo.cs b/PokemonGo/RocketAPI/Window/Properties/AssemblyInfo.cs
index 3402d68..754dab1 100644
--- a/PokemonGo/RocketAPI/Window/Properties/AssemblyInfo.cs
+++ b/PokemonGo/RocketAPI/Window/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.7.0.1")]
+[assembly: AssemblyVersion("1.7.1.1")]
[assembly: AssemblyFileVersion("1.0.0.0")]
You may download the files in Public Git.