├── .gitignore ├── APIConfiguration.cs ├── Authentication ├── Data │ ├── AccessToken.cs │ └── LoginData.cs ├── Exceptions │ └── WrongCredentialsException.cs ├── Login.cs └── Session.cs ├── Client.cs ├── Constants.cs ├── Encrypt ├── Cipher.cs ├── ICrypt.cs ├── Rand.cs └── TwoFish.cs ├── Enums └── AuthType.cs ├── Exceptions ├── APIBadRequestExceltion.cs ├── AccessTokenExpiredException.cs ├── AccountNotVerifiedException.cs ├── AuthConfigException.cs ├── CaptchaException.cs ├── GoogleException.cs ├── GoogleOfflineException.cs ├── GoogleTwoFactorException.cs ├── HasherException.cs ├── InvalidResponseException.cs ├── LoginFailedException.cs ├── MinimumClientVersionException.cs ├── PTCOfflineException.cs ├── PtcLoginException.cs └── SessionInvalidatedException.cs ├── Extensions ├── DateTimeExtensions.cs ├── HttpClientExtensions.cs └── RandomExtension.cs ├── Hash ├── HashModel.cs ├── IHasher.cs └── PokefarmerHasher.cs ├── Helpers ├── CommonRequest.cs ├── DeviceInfoHelper.cs ├── HashBuilder.cs ├── HtmlRemoval.cs ├── HttpClientHelper.cs ├── JsonHelper.cs ├── KillswitchTask.cs ├── LehmerRng.cs ├── PokemonCpUtils.cs ├── PokemonMeta.cs ├── PokemonMetaRegistry.cs ├── PokemonMoveMeta.cs ├── RandomHelper.cs ├── RandomIdGenerator.cs ├── RequestBuilder.cs ├── RequestBuilder.cs.orig ├── RetryHandler.cs ├── S2Helper.cs ├── TimeUtil.cs ├── TimeZone.cs └── Utils.cs ├── HttpClient └── PokemonClient.cs ├── ICaptchaResolver.cs ├── ILogger.cs ├── ISettings.cs ├── LICENSE.md ├── Logging └── HashInfo.cs ├── LoginProviders ├── GoogleLoginProvider.cs ├── ILoginProvider.cs └── PtcLoginProvider.cs ├── PokemonGo.RocketAPI.csproj ├── Properties └── AssemblyInfo.cs ├── README.md ├── Rpc ├── BaseRpc.cs ├── Download.cs ├── Encounter.cs ├── Fort.cs ├── Inventory.cs ├── Login.cs ├── Map.cs ├── Misc.cs └── Player.cs ├── appveyor.yml └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | *.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # and except awesomium, which is not a nuget package 161 | !**/packages/Awesomium.1.7.5.1/ 162 | # Uncomment if necessary however generally it will be regenerated when needed 163 | #!**/packages/repositories.config 164 | # NuGet v3's project.json files produces more ignoreable files 165 | *.nuget.props 166 | *.nuget.targets 167 | 168 | # Microsoft Azure Build Output 169 | csx/ 170 | *.build.csdef 171 | 172 | # Microsoft Azure Emulator 173 | ecf/ 174 | rcf/ 175 | 176 | # Windows Store app package directories and files 177 | AppPackages/ 178 | BundleArtifacts/ 179 | Package.StoreAssociation.xml 180 | _pkginfo.txt 181 | 182 | # Visual Studio cache files 183 | # files ending in .cache can be ignored 184 | *.[Cc]ache 185 | # but keep track of directories ending in .cache 186 | !*.[Cc]ache/ 187 | 188 | # Others 189 | ClientBin/ 190 | ~$* 191 | *~ 192 | *.dbmdl 193 | *.dbproj.schemaview 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | -------------------------------------------------------------------------------- /APIConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI 2 | { 3 | public static class APIConfiguration 4 | { 5 | //TODO : Migrate other configuration to here - or may by TinyIOC is good choice to do binding. 6 | public static ILogger Logger = new DefaultConsoleLogger(); 7 | 8 | public static ICaptchaResolver CaptchaResolver = null; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Authentication/Data/AccessToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | 4 | namespace PokemonGo.RocketAPI.Authentication.Data 5 | { 6 | public class AccessToken 7 | { 8 | [JsonIgnore] 9 | public string Uid => $"{Username}-{ProviderID}"; 10 | 11 | [JsonProperty("username", Required = Required.Always)] 12 | public string Username { get; set; } 13 | 14 | [JsonProperty("token", Required = Required.Always)] 15 | public string Token { get; set; } 16 | 17 | [JsonProperty("expiry", Required = Required.Always)] 18 | public DateTime Expiry { get; set; } 19 | 20 | [JsonProperty("provider_id", Required = Required.Always)] 21 | public string ProviderID { get; set; } 22 | 23 | [JsonIgnore] 24 | public bool IsExpired => DateTime.UtcNow > Expiry; 25 | 26 | //[JsonIgnore] 27 | //public AuthTicket AuthTicket { get; set; } 28 | 29 | public void Expire() 30 | { 31 | Expiry = DateTime.UtcNow; 32 | //AuthTicket = null; 33 | Token = null; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Authentication/Data/LoginData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace PokemonGo.RocketAPI.Authentication.Data 4 | { 5 | internal struct LoginData 6 | { 7 | [JsonProperty("lt", Required = Required.Always)] 8 | public string Lt { get; set; } 9 | 10 | [JsonProperty("execution", Required = Required.Always)] 11 | public string Execution { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Authentication/Exceptions/WrongCredentialsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Authentication.Exceptions 4 | { 5 | public class WrongCredentialsException : Exception 6 | { 7 | 8 | public WrongCredentialsException(string message) : base(message) 9 | { 10 | 11 | } 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Authentication/Login.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using static POGOProtos.Networking.Envelopes.Signature.Types; 4 | using PokemonGo.RocketAPI.Authentication.Data; 5 | using PokemonGo.RocketAPI.LoginProviders; 6 | using GeoCoordinatePortable; 7 | 8 | namespace PokemonGo.RocketAPI.Authentication 9 | { 10 | /// 11 | /// Responsible for Authenticating and Re-authenticating the user. 12 | /// 13 | public static class Login 14 | { 15 | /// 16 | /// Login with a stored . 17 | /// 18 | /// 19 | /// 20 | /// The initial latitude you will spawn at after logging into PokémonGo. 21 | /// The initial longitude you will spawn at after logging into PokémonGo. 22 | /// The used by the , keep null if you want a randomly generated . 23 | /// 24 | public static Session GetSession(ILoginProvider loginProvider, AccessToken accessToken, double initialLatitude, double initialLongitude, DeviceInfo deviceInfo = null) 25 | { 26 | if (accessToken.IsExpired) 27 | throw new Exception("AccessToken is expired."); 28 | 29 | return new Session(loginProvider, accessToken, new GeoCoordinate(initialLatitude, initialLongitude), deviceInfo); 30 | } 31 | 32 | /// 33 | /// Login through OAuth with PTC / Google. 34 | /// 35 | /// The OAuth provider you use to authenticate. 36 | /// The initial latitude you will spawn at after logging into PokémonGo. 37 | /// The initial longitude you will spawn at after logging into PokémonGo. 38 | /// The used by the , keep null if you want a randomly generated . 39 | /// 40 | public static async Task GetSession(ILoginProvider loginProvider, double initialLatitude, double initialLongitude, DeviceInfo deviceInfo = null) 41 | { 42 | return new Session(loginProvider, await loginProvider.GetAccessToken().ConfigureAwait(false), new GeoCoordinate(initialLatitude, initialLongitude), deviceInfo); 43 | } 44 | 45 | /// 46 | /// Login with a stored . 47 | /// 48 | /// The OAuth provider you use to authenticate. 49 | /// The you want to re-use. 50 | /// The initial coordinate you will spawn at after logging into PokémonGo. 51 | /// The used by the , keep null if you want a randomly generated . 52 | /// 53 | public static Session GetSession(ILoginProvider loginProvider, AccessToken accessToken, GeoCoordinate coordinate, DeviceInfo deviceInfo = null) 54 | { 55 | if (accessToken.IsExpired) 56 | { 57 | throw new ArgumentException($"{nameof(accessToken)} is expired."); 58 | } 59 | 60 | //Logger.Debug("Authenticated from cache."); 61 | return new Session(loginProvider, accessToken, coordinate, deviceInfo); 62 | } 63 | 64 | /// 65 | /// Login through OAuth with PTC / Google. 66 | /// 67 | /// The OAuth provider you use to authenticate. 68 | /// The initial coordinate you will spawn at after logging into PokémonGo. 69 | /// The used by the , keep null if you want a randomly generated . 70 | /// 71 | public static async Task GetSession(ILoginProvider loginProvider, GeoCoordinate coordinate, DeviceInfo deviceInfo = null) 72 | { 73 | return new Session(loginProvider, await loginProvider.GetAccessToken().ConfigureAwait(false), coordinate, deviceInfo); 74 | } 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Authentication/Session.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using POGOProtos.Networking.Envelopes; 6 | using POGOProtos.Settings; 7 | using static POGOProtos.Networking.Envelopes.Signature.Types; 8 | using PokemonGo.RocketAPI.Helpers; 9 | using PokemonGo.RocketAPI.Rpc; 10 | using PokemonGo.RocketAPI.Authentication.Data; 11 | using PokemonGo.RocketAPI.LoginProviders; 12 | using PokemonGo.RocketAPI.HttpClient; 13 | using GeoCoordinatePortable; 14 | 15 | namespace PokemonGo.RocketAPI.Authentication 16 | { 17 | /// 18 | /// This is an authenticated with PokémonGo that handles everything between the developer and PokémonGo. 19 | /// 20 | public class Session : IDisposable 21 | { 22 | 23 | /// 24 | /// This is the which is responsible for retrieving events and updating gps location. 25 | /// 26 | //private readonly HeartbeatDispatcher _heartbeat; 27 | 28 | /// 29 | /// This is the which is responsible for all communication between us and PokémonGo. 30 | /// Only use this if you know what you are doing. 31 | /// 32 | //public readonly RpcClient RpcClient; 33 | 34 | private static readonly string[] ValidLoginProviders = { "ptc", "google" }; 35 | 36 | /// 37 | /// Stores data like assets and item templates. Defaults to an in-memory cache, but can be implemented as writing to disk by the platform 38 | /// 39 | // public IDataCache DataCache { get; set; } = new MemoryDataCache(); 40 | // public Templates Templates { get; private set; } 41 | 42 | internal Session(ILoginProvider loginProvider, AccessToken accessToken, GeoCoordinate geoCoordinate, Signature.Types.DeviceInfo deviceInfo = null) 43 | { 44 | if (!ValidLoginProviders.Contains(loginProvider.ProviderId)) 45 | { 46 | throw new ArgumentException($"LoginProvider ID must be one of the following: {string.Join(", ", ValidLoginProviders)}"); 47 | } 48 | 49 | HttpClient = new PokemonHttpClient(); 50 | DeviceInfo = deviceInfo ?? DeviceInfoHelper.GetRandomIosDevice(); 51 | AccessToken = accessToken; 52 | LoginProvider = loginProvider; 53 | // Player = new Player(geoCoordinate); 54 | //Map = new Map(this); 55 | //RpcClient = new RpcClient(this); 56 | //_heartbeat = new HeartbeatDispatcher(this); 57 | } 58 | 59 | /// 60 | /// Gets the of the . 61 | /// 62 | internal Random Random { get; private set; } = new Random(); 63 | 64 | /// 65 | /// Gets the of the . 66 | /// 67 | internal PokemonHttpClient HttpClient { get; } 68 | 69 | /// 70 | /// Gets the used by . 71 | /// 72 | public DeviceInfo DeviceInfo { get; private set; } 73 | 74 | /// 75 | /// Gets the used to obtain an . 76 | /// 77 | private ILoginProvider LoginProvider { get; } 78 | 79 | /// 80 | /// Gets the of the . 81 | /// 82 | public AccessToken AccessToken { get; private set; } 83 | 84 | /// 85 | /// Gets the of the . 86 | /// 87 | public Player Player { get; private set; } 88 | 89 | /// 90 | /// Gets the of the . 91 | /// 92 | public Map Map { get; } 93 | 94 | /// 95 | /// Gets the of the . 96 | /// 97 | public GlobalSettings GlobalSettings { get; internal set; } 98 | 99 | private Semaphore ReauthenticateMutex { get; } = new Semaphore(1, 1); 100 | 101 | /// 102 | /// Ensures the gets reauthenticated, no matter how long it takes. 103 | /// 104 | internal async Task Reauthenticate() 105 | { 106 | ReauthenticateMutex.WaitOne(); 107 | if (AccessToken.IsExpired) 108 | { 109 | AccessToken accessToken = null; 110 | var tries = 0; 111 | while (accessToken == null) 112 | { 113 | try 114 | { 115 | accessToken = await LoginProvider.GetAccessToken().ConfigureAwait(false); 116 | } 117 | catch (Exception ) 118 | { 119 | // Logger.Error($"Reauthenticate exception was catched: {exception}"); 120 | } 121 | finally 122 | { 123 | if (accessToken == null) 124 | { 125 | var sleepSeconds = Math.Min(60, ++tries*5); 126 | // Logger.Error($"Reauthentication failed, trying again in {sleepSeconds} seconds."); 127 | await Task.Delay(TimeSpan.FromMilliseconds(sleepSeconds * 1000)).ConfigureAwait(false); 128 | } 129 | } 130 | } 131 | AccessToken = accessToken; 132 | OnAccessTokenUpdated(); 133 | } 134 | ReauthenticateMutex.Release(); 135 | } 136 | 137 | private void OnAccessTokenUpdated() 138 | { 139 | AccessTokenUpdated?.Invoke(this, EventArgs.Empty); 140 | } 141 | 142 | public event EventHandler AccessTokenUpdated; 143 | 144 | public void Dispose() 145 | { 146 | Dispose(true); 147 | GC.SuppressFinalize(this); 148 | } 149 | 150 | protected virtual void Dispose(bool disposing) 151 | { 152 | if (!disposing) return; 153 | 154 | ReauthenticateMutex?.Dispose(); 155 | //RpcClient?.Dispose(); 156 | HttpClient?.Dispose(); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /Client.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Net; 5 | using PokemonGo.RocketAPI.Enums; 6 | using PokemonGo.RocketAPI.Extensions; 7 | using PokemonGo.RocketAPI.HttpClient; 8 | using PokemonGo.RocketAPI.Rpc; 9 | using POGOProtos.Enums; 10 | using POGOProtos.Networking.Envelopes; 11 | using PokemonGo.RocketAPI.Helpers; 12 | using PokemonGo.RocketAPI.Hash; 13 | using PokemonGo.RocketAPI.Encrypt; 14 | using PokemonGo.RocketAPI.Exceptions; 15 | using PokemonGo.RocketAPI.Authentication.Data; 16 | using PokemonGo.RocketAPI.LoginProviders; 17 | using POGOProtos.Settings; 18 | 19 | #endregion 20 | 21 | namespace PokemonGo.RocketAPI 22 | { 23 | public class Client : ICaptchaResponseHandler 24 | { 25 | public static WebProxy Proxy; 26 | 27 | internal readonly PokemonHttpClient PokemonHttpClient; 28 | public Download Download; 29 | public Encounter Encounter; 30 | public Fort Fort; 31 | public Inventory Inventory; 32 | public Login Login; 33 | public Map Map; 34 | public Misc Misc; 35 | public Player Player; 36 | string CaptchaToken; 37 | public KillSwitchTask KillswitchTask; 38 | public IHasher Hasher; 39 | public ICrypt Cryptor; 40 | internal RequestBuilder RequestBuilder; 41 | 42 | public ISettings Settings { get; } 43 | 44 | public double CurrentLatitude { get; internal set; } 45 | public double CurrentLongitude { get; internal set; } 46 | public double CurrentAltitude { get; internal set; } 47 | public double CurrentAccuracy { get; internal set; } 48 | public float CurrentSpeed { get; internal set; } 49 | 50 | public AuthType AuthType 51 | { get { return Settings.AuthType; } set { Settings.AuthType = value; } } 52 | 53 | internal string ApiUrl { get; set; } 54 | internal AuthTicket AuthTicket { get; set; } 55 | 56 | internal string SettingsHash { get; set; } 57 | public GlobalSettings GlobalSettings { get; set; } 58 | public long InventoryLastUpdateTimestamp { get; set; } 59 | internal Platform Platform { get; set; } 60 | internal uint AppVersion { get; set; } 61 | internal string UnknownPlat8Field { get; set; } 62 | internal long Unknown25 { get; set; } 63 | public string ApiEndPoint { get; set; } 64 | public long StartTime { get; set; } 65 | public Version CurrentApiEmulationVersion { get; set; } 66 | public Version MinimumClientVersion { get; set; } // This is version from DownloadSettings, but after login is updated from https://pgorelease.nianticlabs.com/plfe/version 67 | 68 | //public POGOLib.Net.Session AuthSession { get; set; } 69 | public ILoginProvider LoginProvider { get; set; } 70 | public AccessToken AccessToken { get; set; } 71 | //For Test 72 | //public int PageOffset { get; set; } 73 | 74 | public Client(ISettings settings) 75 | { 76 | if (settings.UsePogoDevHashServer || settings.UseCustomAPI) 77 | { 78 | if (string.IsNullOrEmpty(settings.AuthAPIKey)) throw new AuthConfigException("You have selected Pogodev API but not provide proper API Key"); 79 | 80 | Cryptor = new Cipher(); 81 | 82 | ApiEndPoint = Constants.ApiEndPoint; 83 | 84 | Hasher = new PokefarmerHasher(settings, settings.AuthAPIKey, settings.DisplayVerboseLog, ApiEndPoint); 85 | 86 | // These 4 constants below need to change if we update the hashing server API version that is used. 87 | Unknown25 = Constants.Unknown25; 88 | 89 | // WARNING! IF YOU CHANGE THE APPVERSION BELOW ALSO UPDATE THE API_VERSION AT THE TOP OF THE FILE! 90 | AppVersion = Constants.AppVersion; 91 | 92 | CurrentApiEmulationVersion = new Version(Constants.API_VERSION); 93 | UnknownPlat8Field = Constants.UnknownPlat8Field; 94 | } 95 | /* 96 | else 97 | if (settings.UseLegacyAPI) 98 | { 99 | Hasher = new LegacyHashser(); 100 | Cryptor = new LegacyCrypt(); 101 | 102 | Unknown25 = -816976800928766045;// - 816976800928766045;// - 1553869577012279119; 103 | AppVersion = 4500; 104 | CurrentApiEmulationVersion = new Version("0.45.0"); 105 | } 106 | */ 107 | else 108 | { 109 | throw new AuthConfigException("No API method was selected in auth.json"); 110 | } 111 | 112 | Settings = settings; 113 | Proxy = InitProxy(); 114 | PokemonHttpClient = new PokemonHttpClient(); 115 | Login = new Login(this); 116 | Player = new Player(this); 117 | Download = new Download(this); 118 | Inventory = new Inventory(this); 119 | Map = new Map(this); 120 | Fort = new Fort(this); 121 | Encounter = new Encounter(this); 122 | Misc = new Misc(this); 123 | KillswitchTask = new KillSwitchTask(this); 124 | 125 | Player.SetCoordinates(Settings.DefaultLatitude, Settings.DefaultLongitude, Settings.DefaultAltitude); 126 | Platform = settings.DevicePlatform.Equals("ios", StringComparison.Ordinal) ? Platform.Ios : Platform.Android; 127 | 128 | // We can no longer emulate Android so for now just overwrite settings with randomly generated iOS device info. 129 | if (Platform == Platform.Android) 130 | { 131 | Signature.Types.DeviceInfo iosInfo = DeviceInfoHelper.GetRandomIosDevice(); 132 | settings.DeviceId = iosInfo.DeviceId; 133 | settings.DeviceBrand = iosInfo.DeviceBrand; 134 | settings.DeviceModel = iosInfo.DeviceModel; 135 | settings.DeviceModelBoot = iosInfo.DeviceModelBoot; 136 | settings.HardwareManufacturer = iosInfo.HardwareManufacturer; 137 | settings.HardwareModel = iosInfo.HardwareModel; 138 | settings.FirmwareBrand = iosInfo.FirmwareBrand; 139 | settings.FirmwareType = iosInfo.FirmwareType; 140 | 141 | // Clear out the android fields. 142 | settings.AndroidBoardName = ""; 143 | settings.AndroidBootloader = ""; 144 | settings.DeviceModelIdentifier = ""; 145 | settings.FirmwareTags = ""; 146 | settings.FirmwareFingerprint = ""; 147 | 148 | // Now set the client platform to ios 149 | Platform = Platform.Ios; 150 | } 151 | } 152 | 153 | public void Reset() 154 | { 155 | AccessToken = null; 156 | AuthTicket = null; 157 | StartTime = Utils.GetTime(true); 158 | RequestBuilder = new RequestBuilder(this, this.Settings); 159 | InventoryLastUpdateTimestamp = 0; 160 | SettingsHash = ""; 161 | } 162 | 163 | public void SetCaptchaToken(string token) 164 | { 165 | CaptchaToken = token; 166 | } 167 | 168 | private WebProxy InitProxy() 169 | { 170 | if (!Settings.UseProxy) return null; 171 | 172 | var uri = $"http://{Settings.UseProxyHost}:{Settings.UseProxyPort}"; 173 | var prox = new WebProxy(new Uri(uri), false, null); 174 | 175 | if (Settings.UseProxyAuthentication) 176 | prox.Credentials = new NetworkCredential(Settings.UseProxyUsername, Settings.UseProxyPassword); 177 | return prox; 178 | } 179 | 180 | public bool CheckCurrentVersionOutdated() 181 | { 182 | if (MinimumClientVersion == null) 183 | return false; 184 | 185 | return CurrentApiEmulationVersion < MinimumClientVersion; 186 | } 187 | 188 | public static Version GetMinimumRequiredVersionFromUrl() 189 | { 190 | try 191 | { 192 | var Client = new WebClient(); 193 | var version = Client.DownloadString(Constants.VersionUrl).Replace("\u0006", "").Replace("\n", ""); 194 | return new Version(version); 195 | } 196 | catch (Exception ex) 197 | { 198 | var errorMessage = $"The Niantic version check URL ({Constants.VersionUrl}) is returning the following error(s): {ex.Message}"; 199 | APIConfiguration.Logger.LogError(errorMessage); 200 | } 201 | return null; 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /Constants.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | 4 | namespace PokemonGo.RocketAPI 5 | { 6 | public static class Constants 7 | { 8 | // API stuff 9 | 10 | public const string ApiUrl = "https://pgorelease.nianticlabs.com/plfe/rpc"; 11 | 12 | public const string VersionUrl = "https://pgorelease.nianticlabs.com/plfe/version"; 13 | 14 | // Login stuff 15 | 16 | public const string LoginUrl = "https://sso.pokemon.com/sso/login?service=https%3A%2F%2Fsso.pokemon.com%2Fsso%2Foauth2.0%2FcallbackAuthorize"; 17 | 18 | public const string LoginUserAgent = "pokemongo/1 CFNetwork/897.1 Darwin/17.5.0"; //iOS 11.3.0 19 | public const string LoginManufactor = "X-Unity-Version"; 20 | public const string LoginManufactorVersion = "2017.1.2f1"; //"5.5.1f1";//"5.6.1f1"; 21 | public const string LoginHostValue = "sso.pokemon.com"; 22 | public const string LoginOauthUrl = "https://sso.pokemon.com/sso/oauth2.0/accessToken"; 23 | public const string Connection = "keep-alive"; 24 | public const string AcceptLanguage = "en-US"; 25 | public const string AcceptEncoding = "gzip-deflate"; 26 | public const string Accept = "*/*"; 27 | public const string Client_Id = "mobile-app_pokemon-go"; 28 | public const string Redirect_Uri = "https://www.nianticlabs.com/pokemongo/error"; 29 | public const string Client_Secret = "w8ScCUXJQc6kXKw8FiOhd8Fixzht18Dq3PEVkUCP5ZPxtgyWsbTvWHFLm2wNY0JR"; 30 | public const string Grant_Type = "refresh_token"; 31 | public static TimeSpan TimeOut = new TimeSpan(0,10,0); 32 | 33 | public const string GoogleAuthService = "audience:server:client_id:848232511240-7so421jotr2609rmqakceuu1luuq0ptb.apps.googleusercontent.com"; 34 | public const string GoogleAuthApp = "com.nianticlabs.pokemongo"; 35 | public const string GoogleAuthClientSig = "321187995bc7cdc2b5fc91b11a96e2baa8602c62"; 36 | 37 | public const string ApiEndPoint = "api/v159_1/hash"; 38 | public const string API_VERSION = "0.95.3"; 39 | public const uint AppVersion = 9500; 40 | public const long Unknown25 = unchecked((long)0xF522F8878F08FFD6); 41 | public const string UnknownPlat8Field = "d9ad6deadadfd59abc0e8a85d01e2f6140e6e79b"; 42 | 43 | // Login PTC 44 | 45 | public const string RpcUrl = ApiUrl; 46 | 47 | public const string NumberedRpcUrl = @"https://pgorelease.nianticlabs.com/plfe/{0}/rpc"; 48 | 49 | public const string PtcLoginUrl = LoginUrl; 50 | 51 | public const string PtcLoginOauth = LoginOauthUrl; 52 | public const string GoogleGrantRefreshAccessUrl = "https://android.clients.google.com/auth"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Encrypt/Cipher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Encrypt 4 | { 5 | /// 6 | /// Description of Cipher. 7 | /// 8 | public class Cipher : ICrypt 9 | { 10 | // This little function is an "extra" "encryption" on top of the UK 6 encryption. 11 | // 12 | // Integrity byte changed to 0x23. 13 | private static readonly byte[] KEY = { 14 | 0x4F, 0xEB, 0x1C, 0xA5, 0xF6, 0x1A, 0x67, 0xCE, 15 | 0x43, 0xF3, 0xF0, 0x0C, 0xB1, 0x23, 0x88, 0x35, 16 | 0xE9, 0x8B, 0xE8, 0x39, 0xD8, 0x89, 0x8F, 0x5A, 17 | 0x3B, 0x51, 0x2E, 0xA9, 0x47, 0x38, 0xC4, 0x14 18 | }; 19 | 20 | private byte[] xbox = { 21 | 0x01, 22 | 0x00, 23 | 0x83, 0x57, 0x47, 0x28, 0x1c, 0x84, 0x5c, 0xf0, 0x25, 0xcc, 0x14, 0xd1, 0xe4, 0xe0, 0x4b, 0x4c, 24 | 0x68, 0x20, 0x72, 0x37, 0x34, 0x7b, 0x23, 0xf3, 0x7d, 0x62, 0x8c, 0xa7, 0xe2, 0xa8, 0x88, 0x6e, 25 | 0x27, 0x74, 0x3e, 0x94, 0x2a, 0x6d, 0x3b, 0xa5, 0x7a, 0x41, 0xa3, 0x13, 0x8b, 0x31, 0x42, 0x09, 26 | 0xb4, 0x16, 0x2f, 0xb7, 0x06, 0x04, 0x75, 0x39, 0x67, 0xc0, 0x30, 0xde, 0xa4, 0xf8, 0xd8, 0x19, 27 | 0xf7, 0xf9, 0x2d, 0xae, 0xc2, 0xe9, 0xcb, 0xc1, 0x1b, 0x5e, 0xc3, 0x08, 0xaa, 0x4f, 0xd4, 0xbf, 28 | 0x35, 0x63, 0x2e, 0x8f, 0x9f, 0x0f, 0x8a, 0x97, 0xb8, 0x3a, 0xa6, 0x48, 0x98, 0x11, 0x71, 0x89, 29 | 0x6c, 0x9b, 0x0a, 0x61, 0xa9, 0x86, 0x22, 0xe3, 0x03, 0x7f, 0x4a, 0x99, 0x00, 0xab, 0xed, 0xf2, 30 | 0x9a, 0xba, 0x52, 0x29, 0x1e, 0xbe, 0xfc, 0xa0, 0x65, 0x6a, 0x78, 0xca, 0x69, 0xd0, 0x21, 0x49, 31 | 0xbd, 0x4d, 0x2c, 0x7e, 0x53, 0xb5, 0xe6, 0xdc, 0x60, 0x8e, 0xfd, 0x17, 0x82, 0x0e, 0x9c, 0x4e, 32 | 0xaf, 0xc5, 0xc4, 0x5d, 0x81, 0xf4, 0x02, 0x5b, 0x0b, 0x50, 0xac, 0x45, 0x95, 0x5f, 0x38, 0xd3, 33 | 0x76, 0xc7, 0x07, 0x90, 0x92, 0x79, 0x15, 0x77, 0xdb, 0x12, 0x3d, 0xbc, 0x10, 0x1a, 0x51, 0xb9, 34 | 0x32, 0xbb, 0x26, 0x56, 0xdd, 0xd9, 0xe5, 0x7c, 0xe8, 0xe7, 0xad, 0xd2, 0xf6, 0xee, 0xcf, 0xfe, 35 | 0x87, 0x66, 0x64, 0xf5, 0xcd, 0xe1, 0xc9, 0xfa, 0x0c, 0x01, 0x6b, 0x3f, 0x0d, 0xda, 0x96, 0x40, 36 | 0xa2, 0x1f, 0x5a, 0x24, 0xeb, 0x59, 0xec, 0x44, 0x43, 0x91, 0xb0, 0xb2, 0xd7, 0x54, 0x2b, 0xce, 37 | 0x33, 0xff, 0x58, 0x18, 0x93, 0x46, 0xc8, 0xdf, 0x3c, 0xfb, 0x8d, 0xb1, 0x55, 0xd5, 0x6f, 0x70, 38 | 0xef, 0x9d, 0xa1, 0x9e, 0xb6, 0xea, 0xc6, 0xf1, 0x80, 0x1d, 0x05, 0x73, 0xd6, 0xb3, 0x36, 0x85 39 | }; 40 | 41 | private void encrypt_cipher(byte[] src, int size) 42 | { 43 | var newxbox = new byte[xbox.Length]; 44 | xbox.CopyTo(newxbox, 0); 45 | 46 | int a4 = size - 1; 47 | int srci = 0; 48 | byte v4 = newxbox[0]; 49 | byte v5 = newxbox[1]; 50 | for (; a4 != 0; v4 = (byte)(v4 + 1)) 51 | { 52 | --a4; 53 | 54 | byte v7 = newxbox[2 + v4]; 55 | v5 = (byte)(v5 + v7); 56 | byte v9 = newxbox[2 + v5]; 57 | newxbox[2 + v4] = v9; 58 | newxbox[2 + v5] = v7; 59 | byte v10 = (byte)(v9 + v7); 60 | src[srci++] ^= newxbox[2 + v10]; 61 | } 62 | newxbox[0] = v4; 63 | newxbox[1] = v5; 64 | } 65 | 66 | public byte[] Encrypt(byte[] uncryptedSignature, uint msSinceStart) 67 | { 68 | var rnd = new Rand(msSinceStart); 69 | 70 | object[] key = TwoFish.MakeKey(KEY); 71 | 72 | var xor_byte = new byte[TwoFish.BLOCK_SIZE]; 73 | 74 | for (int i = 0; i < TwoFish.BLOCK_SIZE; ++i) 75 | xor_byte[i] = (byte)rnd.Next(); 76 | 77 | int block_count = (uncryptedSignature.Length + 256) / 256; 78 | int output_size = 4 + (block_count * 256) + 1; 79 | var output = new byte[output_size]; 80 | output[0] = (byte)(msSinceStart >> 24); 81 | output[1] = (byte)(msSinceStart >> 16); 82 | output[2] = (byte)(msSinceStart >> 8); 83 | output[3] = (byte)msSinceStart; 84 | 85 | 86 | Array.Copy(uncryptedSignature, 0, output, 4, uncryptedSignature.Length); 87 | 88 | output[output_size - 2] = (byte)(256 - uncryptedSignature.Length % 256); 89 | 90 | 91 | for (int offset = 0; offset < block_count * 256; offset += TwoFish.BLOCK_SIZE) 92 | { 93 | for (int i = 0; i < TwoFish.BLOCK_SIZE; i++) 94 | output[4 + offset + i] ^= xor_byte[i]; 95 | 96 | byte[] block = TwoFish.BlockEncrypt(output, offset + 4, key); 97 | Array.Copy(block, 0, output, offset + 4, block.Length); 98 | Array.Copy(output, 4 + offset, xor_byte, 0, TwoFish.BLOCK_SIZE); 99 | 100 | } 101 | 102 | // ------------------------------------- NEW CODE START HERE GUYS 103 | 104 | // Integrity byte is 0x23 since 05/01/2017. 105 | output[output_size - 1] = 0x23; 106 | 107 | // This new "on top" encryption is there since 05/01/2017. 108 | encrypt_cipher(output, output_size); 109 | 110 | return output; 111 | } 112 | } 113 | } -------------------------------------------------------------------------------- /Encrypt/ICrypt.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI.Encrypt 2 | { 3 | public interface ICrypt 4 | { 5 | byte[] Encrypt(byte[] input, uint ms); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Encrypt/Rand.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI.Encrypt 2 | { 3 | /// 4 | /// Description of Rand. 5 | /// 6 | public class Rand 7 | { 8 | private long state; 9 | 10 | public Rand(long state) 11 | { 12 | this.state = state; 13 | } 14 | 15 | public byte Next() 16 | { 17 | state = (state * 0x41C64E6D) + 0x3039; 18 | return (byte)((state >> 16) & 0xFF); 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /Enums/AuthType.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI.Enums 2 | { 3 | public enum AuthType 4 | { 5 | Google, 6 | Ptc 7 | } 8 | } -------------------------------------------------------------------------------- /Exceptions/APIBadRequestExceltion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class APIBadRequestException:Exception 6 | { 7 | public APIBadRequestException(string message) : base(message) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Exceptions/AccessTokenExpiredException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class AccessTokenExpiredException : Exception 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Exceptions/AccountNotVerifiedException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class AccountNotVerifiedException : Exception 10 | { 11 | public AccountNotVerifiedException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Exceptions/AuthConfigException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class AuthConfigException : Exception 6 | { 7 | public AuthConfigException(string message): base(message) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Exceptions/CaptchaException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class CaptchaException : Exception 6 | { 7 | public string Url { get; set; } 8 | 9 | public CaptchaException(string url) 10 | { 11 | Url = url; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Exceptions/GoogleException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class GoogleException : Exception 10 | { 11 | public GoogleException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Exceptions/GoogleOfflineException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class GoogleOfflineException : Exception 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Exceptions/GoogleTwoFactorException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class GoogleTwoFactorException : Exception 10 | { 11 | public GoogleTwoFactorException(string message) : base(message) 12 | { 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Exceptions/HasherException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class HasherException :Exception 6 | { 7 | public HasherException(string message): base(message) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Exceptions/InvalidResponseException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class InvalidResponseException : Exception 10 | { 11 | public InvalidResponseException() 12 | { 13 | } 14 | 15 | public InvalidResponseException(string message) 16 | : base(message) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Exceptions/LoginFailedException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | 10 | public class LoginFailedException : Exception 11 | { 12 | 13 | public LoginFailedException() 14 | { 15 | } 16 | 17 | public LoginFailedException(string message) : base(message) 18 | { 19 | } 20 | } 21 | 22 | public class TokenRefreshException : Exception 23 | { 24 | 25 | public TokenRefreshException() 26 | { 27 | } 28 | 29 | public TokenRefreshException(string message) : base(message) 30 | { 31 | } 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /Exceptions/MinimumClientVersionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class MinimumClientVersionException : Exception 6 | { 7 | public Version CurrentApiVersion; 8 | public Version MinimumClientVersion; 9 | public MinimumClientVersionException(Version currentApiVersion, Version minimumClientVersion) : base() 10 | { 11 | CurrentApiVersion = currentApiVersion; 12 | MinimumClientVersion = minimumClientVersion; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Exceptions/PTCOfflineException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | public class PtcOfflineException : Exception 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /Exceptions/PtcLoginException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Exceptions 4 | { 5 | public class PtcLoginException : Exception 6 | { 7 | public PtcLoginException(string message) : base(message) { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Exceptions/SessionInvalidatedException.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Exceptions 8 | { 9 | 10 | public class SessionInvalidatedException : Exception 11 | { 12 | public SessionInvalidatedException() 13 | { 14 | } 15 | 16 | public SessionInvalidatedException(string message) : base(message) 17 | { 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Extensions/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Extensions 8 | { 9 | public static class DateTimeExtensions 10 | { 11 | public static long ToUnixTime(this DateTime date) 12 | { 13 | var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 14 | return Convert.ToInt64((date - epoch).TotalMilliseconds); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Extensions/HttpClientExtensions.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using Google.Protobuf; 7 | using PokemonGo.RocketAPI.Exceptions; 8 | using POGOProtos.Networking.Envelopes; 9 | using System.Collections.Concurrent; 10 | using System.Threading; 11 | using Newtonsoft.Json; 12 | using PokemonGo.RocketAPI.Helpers; 13 | 14 | #endregion 15 | 16 | namespace PokemonGo.RocketAPI.Extensions 17 | { 18 | public enum ApiOperation 19 | { 20 | Retry, 21 | Abort 22 | } 23 | 24 | public interface ICaptchaResponseHandler 25 | { 26 | void SetCaptchaToken(string captchaToken); 27 | } 28 | 29 | public static class HttpClientExtensions 30 | { 31 | public static async Task PostProtoPayload(this System.Net.Http.HttpClient client, 32 | Client apiClient, RequestEnvelope requestEnvelope, 33 | params Type[] responseTypes) where TRequest : IMessage 34 | { 35 | var result = new IMessage[responseTypes.Length]; 36 | for (var i = 0; i < responseTypes.Length; i++) 37 | { 38 | result[i] = Activator.CreateInstance(responseTypes[i]) as IMessage; 39 | if (result[i] == null) 40 | { 41 | throw new ArgumentException($"ResponseType {i} is not an IMessage"); 42 | } 43 | } 44 | 45 | ResponseEnvelope response = await PerformThrottledRemoteProcedureCall(client, apiClient, requestEnvelope).ConfigureAwait(false); 46 | 47 | if (response== null || (response.Returns.Count != requestEnvelope.Requests.Count)) 48 | throw new InvalidResponseException($"Error with API request type: {requestEnvelope.Requests[0].RequestType}"); 49 | 50 | for (var i = 0; i < responseTypes.Length; i++) 51 | { 52 | var payload = response.Returns[i]; 53 | result[i].MergeFrom(payload); 54 | } 55 | return result; 56 | } 57 | 58 | public static async Task PostProtoPayload( 59 | this System.Net.Http.HttpClient client, 60 | Client apiClient, RequestEnvelope requestEnvelope) 61 | where TRequest : IMessage 62 | where TResponsePayload : IMessage, new() 63 | { 64 | ResponseEnvelope response = await PerformThrottledRemoteProcedureCall(client, apiClient, requestEnvelope).ConfigureAwait(false); 65 | 66 | if (response.Returns.Count != requestEnvelope.Requests.Count) 67 | throw new InvalidResponseException($"Error with API request type: {requestEnvelope.Requests[0].RequestType}"); 68 | 69 | //Decode payload 70 | //todo: multi-payload support 71 | var payload = response.Returns[0]; 72 | var parsedPayload = new TResponsePayload(); 73 | parsedPayload.MergeFrom(payload); 74 | 75 | return parsedPayload; 76 | } 77 | 78 | private static async Task PerformRemoteProcedureCall(this System.Net.Http.HttpClient client, 79 | Client apiClient, 80 | RequestEnvelope requestEnvelope) where TRequest : IMessage 81 | { 82 | // Check killswitch from url before making API calls. 83 | //renamed to UseCustomAPI if (!apiClient.Settings.UseLegacyAPI) 84 | //{ 85 | if (apiClient.CheckCurrentVersionOutdated()) 86 | throw new MinimumClientVersionException(apiClient.CurrentApiEmulationVersion, apiClient.MinimumClientVersion); 87 | //} 88 | 89 | //Encode payload and put in envelop, then send 90 | var data = requestEnvelope.ToByteString(); 91 | var result = await client.PostAsync(apiClient.ApiUrl, new ByteArrayContent(data.ToByteArray())).ConfigureAwait(false); 92 | 93 | //Decode message 94 | var responseData = await result.Content.ReadAsByteArrayAsync().ConfigureAwait(false); 95 | var codedStream = new CodedInputStream(responseData); 96 | ResponseEnvelope serverResponse = new ResponseEnvelope(); 97 | serverResponse.MergeFrom(codedStream); 98 | 99 | // Process Platform8Response 100 | CommonRequest.ProcessPlatform8Response(apiClient, serverResponse); 101 | 102 | if (!string.IsNullOrEmpty(serverResponse.ApiUrl)) 103 | apiClient.ApiUrl = "https://" + serverResponse.ApiUrl + "/rpc"; 104 | 105 | if (serverResponse.AuthTicket != null) 106 | { 107 | if (serverResponse.AuthTicket.ExpireTimestampMs > (ulong)Utils.GetTime(true)) 108 | { 109 | apiClient.AuthTicket = serverResponse.AuthTicket; 110 | } 111 | else 112 | { 113 | // Expired auth ticket. 114 | apiClient.AuthTicket = null; 115 | } 116 | } 117 | 118 | switch (serverResponse.StatusCode) 119 | { 120 | case ResponseEnvelope.Types.StatusCode.InvalidAuthToken: 121 | await apiClient.RequestBuilder.RegenerateRequestEnvelopeWithNewAccessToken(requestEnvelope).ConfigureAwait(false); 122 | return await PerformRemoteProcedureCall(client, apiClient, requestEnvelope).ConfigureAwait(false); 123 | case ResponseEnvelope.Types.StatusCode.Redirect: 124 | // 53 means that the api_endpoint was not correctly set, should be at this point, though, so redo the request 125 | return await PerformRemoteProcedureCall(client, apiClient, requestEnvelope).ConfigureAwait(false); 126 | case ResponseEnvelope.Types.StatusCode.BadRequest: 127 | // Your account may be banned! please try from the official client. 128 | throw new APIBadRequestException("BAD REQUEST \r\n" + JsonConvert.SerializeObject(requestEnvelope)); 129 | case ResponseEnvelope.Types.StatusCode.Unknown: 130 | break; 131 | case ResponseEnvelope.Types.StatusCode.Ok: 132 | break; 133 | case ResponseEnvelope.Types.StatusCode.OkRpcUrlInResponse: 134 | break; 135 | case ResponseEnvelope.Types.StatusCode.InvalidRequest: 136 | break; 137 | case ResponseEnvelope.Types.StatusCode.InvalidPlatformRequest: 138 | break; 139 | case ResponseEnvelope.Types.StatusCode.SessionInvalidated: 140 | throw new SessionInvalidatedException("SESSION INVALIDATED EXCEPTION"); 141 | default: 142 | throw new ArgumentOutOfRangeException(); 143 | } 144 | return serverResponse; 145 | } 146 | 147 | // RPC Calls need to be throttled 148 | private static long lastRpc = 0; // Starting at 0 to allow first RPC call to be done immediately 149 | private const int minDiff = 1000; // Derived by trial-and-error. Up to 900 can cause server to complain. 150 | private static ConcurrentQueue rpcQueue = new ConcurrentQueue(); 151 | private static ConcurrentDictionary responses = new ConcurrentDictionary(); 152 | private static Semaphore mutex = new Semaphore(1, 1); 153 | 154 | public static async Task PerformThrottledRemoteProcedureCall(this System.Net.Http.HttpClient client, Client apiClient, RequestEnvelope requestEnvelope) where TRequest : IMessage 155 | { 156 | #pragma warning disable IDE0018 // Inline variable declaration - Build.Bat Error Happens if We Do 157 | rpcQueue.Enqueue(requestEnvelope); 158 | var count = rpcQueue.Count; 159 | RequestEnvelope r; 160 | ResponseEnvelope ret; 161 | try 162 | { 163 | mutex.WaitOne(); 164 | while (rpcQueue.TryDequeue(out r)) 165 | { 166 | var diff = Math.Max(0, DateTime.Now.Millisecond - lastRpc); 167 | if (diff < minDiff) 168 | { 169 | var delay = (minDiff - diff) + (int)(new Random().NextDouble() * 0); // Add some randomness 170 | await Task.Delay((int)(delay)).ConfigureAwait(false); 171 | } 172 | lastRpc = DateTime.Now.Millisecond; 173 | ResponseEnvelope response = await PerformRemoteProcedureCall(client, apiClient, r).ConfigureAwait(false); 174 | responses.GetOrAdd(r, response); 175 | } 176 | responses.TryRemove(requestEnvelope, out ret); 177 | } 178 | finally 179 | { 180 | mutex.Release(); 181 | } 182 | return ret; 183 | #pragma warning restore IDE0018 // Inline variable declaration - Build.Bat Error Happens if We Do 184 | } 185 | } 186 | } -------------------------------------------------------------------------------- /Extensions/RandomExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace System 4 | { 5 | public static class RandomExtension 6 | { 7 | 8 | public static double NextDouble(this Random random, double minimum, double maximum) 9 | { 10 | return random.NextDouble() * (maximum - minimum) + minimum; 11 | } 12 | 13 | public static string NextHexNumber(this Random random, int length) 14 | { 15 | var buffer = new byte[length / 2]; 16 | random.NextBytes(buffer); 17 | 18 | var result = string.Concat(buffer.Select(x => x.ToString("X2")).ToArray()); 19 | if (length % 2 == 0) 20 | return result; 21 | 22 | return result + random.Next(16).ToString("X"); 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Hash/HashModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace PokemonGo.RocketAPI.Hash 5 | { 6 | 7 | public class HashResponseContent 8 | { 9 | public uint LocationAuthHash { get; set; } 10 | public uint LocationHash { get; set; } 11 | 12 | // Note: These are actually "unsigned" values. They are sent as signed values simply due to JSON format specifications. 13 | // You should re-cast these to unsigned variants (or leave them as-is in their byte form) 14 | public List RequestHashes { get; set; } 15 | } 16 | 17 | public class HashRequestContent 18 | { 19 | /// 20 | /// The timestamp for the packet being sent to Niantic. This much match what you use in the SignalLog and RpcRequest 21 | /// protos! (EpochTimestampMS) 22 | /// 23 | public ulong Timestamp { get; set; } 24 | 25 | /// 26 | /// The Latitude field from your ClientRpc request envelope. (The one you will be sending to Niantic) 27 | /// For safety reasons, this should also match your last LocationUpdate entry in the SignalLog 28 | /// 29 | public Int64 Latitude64 { get; set; } 30 | /// 31 | /// The Longitude field from your ClientRpc request envelope. (The one you will be sending to Niantic) 32 | /// For safety reasons, this should also match your last LocationUpdate entry in the SignalLog 33 | /// 34 | public Int64 Longitude64 { get; set; } 35 | /// 36 | /// The Altitude field from your ClientRpc request envelope. (The one you will be sending to Niantic) 37 | /// For safety reasons, this should also match your last LocationUpdate entry in the SignalLog 38 | /// 39 | public Int64 Accuracy64 { get; set; } 40 | 41 | /// 42 | /// The Niantic-specific auth ticket data. 43 | /// 44 | public byte[] AuthTicket { get; set; } 45 | 46 | /// 47 | /// Also known as the "replay check" field. (Field 22 in SignalLog) 48 | /// 49 | public byte[] SessionData { get; set; } 50 | 51 | /// 52 | /// A collection of the request data to be hashed. 53 | /// 54 | public List Requests { get; set; } = new List(); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Hash/IHasher.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace PokemonGo.RocketAPI.Hash 4 | { 5 | public interface IHasher 6 | { 7 | Task RequestHashesAsync(HashRequestContent request); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Hash/PokefarmerHasher.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using PokemonGo.RocketAPI.Exceptions; 3 | using PokemonGo.RocketAPI.Helpers; 4 | using PokemonGo.RocketAPI.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace PokemonGo.RocketAPI.Hash 16 | { 17 | public class PokefarmerHasher : IHasher 18 | { 19 | public static string PokeHashURL = "https://pokehash.buddyauth.com/"; 20 | public static string PokeHashURL2 = "http://pokehash.buddyauth.com/"; 21 | 22 | public class Stat 23 | { 24 | public DateTime Timestamp { get; set; } 25 | public long ResponseTime { get; set; } 26 | } 27 | private string apiKey; 28 | private List> apiKeys = null; 29 | public bool VerboseLog { get; set; } 30 | private List statistics = new List(); 31 | private string apiEndPoint; 32 | 33 | private HashInfo fullStats = new HashInfo(); 34 | 35 | public PokefarmerHasher(ISettings settings, string apiKey, bool log, string apiEndPoint) 36 | { 37 | VerboseLog = log; 38 | this.apiKey = apiKey; 39 | this.apiEndPoint = apiEndPoint; 40 | if (settings.UseCustomAPI) 41 | { 42 | PokeHashURL = settings.UrlHashServices; 43 | PokeHashURL2 = settings.UrlHashServices; 44 | if (!string.IsNullOrEmpty(settings.EndPoint)) 45 | this.apiEndPoint = settings.EndPoint; 46 | } 47 | } 48 | 49 | public async Task RequestHashesAsync(HashRequestContent request) 50 | { 51 | int retry = 3; 52 | do 53 | { 54 | try 55 | { 56 | return await InternalRequestHashesAsync(request).ConfigureAwait(false); 57 | } 58 | catch (HasherException hashEx) 59 | { 60 | throw hashEx; 61 | } 62 | catch (TimeoutException) 63 | { 64 | throw new HasherException("Pokefamer Hasher server might down - timeout out"); 65 | } 66 | catch (Exception ex) 67 | { 68 | APIConfiguration.Logger.LogDebug(ex.Message); 69 | } 70 | finally 71 | { 72 | retry--; 73 | } 74 | await Task.Delay(1000).ConfigureAwait(false); 75 | } while (retry > 0); 76 | 77 | throw new HasherException("Pokefamer Hash API server might be down"); 78 | 79 | } 80 | private DateTime lastPrintVerbose = DateTime.Now; 81 | 82 | private async Task InternalRequestHashesAsync(HashRequestContent request) 83 | { 84 | string key = GetAPIKey(); 85 | 86 | var maskedKey = key.Substring(0, 4) + "".PadLeft(key.Length - 8, 'X') + key.Substring(key.Length - 4, 4); 87 | // NOTE: This is really bad. Don't create new HttpClient's all the time. 88 | // Use a single client per-thread if you need one. 89 | using (var client = new System.Net.Http.HttpClient()) 90 | { 91 | // The URL to the hashing server. 92 | // Do not include "api/v1/hash" unless you know why you're doing it, and want to modify this code. 93 | client.BaseAddress = new Uri(PokeHashURL); 94 | 95 | // By default, all requests (and this example) are in JSON. 96 | client.DefaultRequestHeaders.Accept.Clear(); 97 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 98 | // Set the X-AuthToken to the key you purchased from Bossland GmbH 99 | client.DefaultRequestHeaders.Add("X-AuthToken", key); 100 | 101 | var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.ASCII, "application/json"); 102 | // An odd bug with HttpClient. You need to set the content type again. 103 | content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 104 | 105 | Stopwatch watcher = new Stopwatch(); 106 | HttpResponseMessage response = null; 107 | watcher.Start(); 108 | Stat stat = new Stat() { Timestamp = DateTime.Now }; 109 | try 110 | { 111 | response = await client.PostAsync(apiEndPoint, content).ConfigureAwait(false); 112 | } 113 | catch (Exception) 114 | { 115 | try 116 | { 117 | client.BaseAddress = new Uri(PokeHashURL2); 118 | response = await client.PostAsync(apiEndPoint, content).ConfigureAwait(false); 119 | } 120 | catch (Exception ex) 121 | { 122 | throw ex; 123 | } 124 | } 125 | finally 126 | { 127 | watcher.Stop(); 128 | 129 | // Need to check for null response. 130 | if (response == null) 131 | throw new HasherException($"Hash API server ({client.BaseAddress}{apiEndPoint}) might down!"); 132 | 133 | fullStats.APICalles++; 134 | fullStats.TotalTimes += watcher.ElapsedMilliseconds; 135 | 136 | stat.ResponseTime = watcher.ElapsedMilliseconds; 137 | statistics.Add(stat); 138 | statistics.RemoveAll(x => x.Timestamp < DateTime.Now.AddMinutes(-1)); 139 | if (statistics.Count > 0) 140 | { 141 | lastPrintVerbose = DateTime.Now; 142 | double agv = statistics.Sum(x => x.ResponseTime) / statistics.Count; 143 | fullStats.Last60MinAPICalles = statistics.Count; 144 | fullStats.Last60MinAPIAvgTime = agv; 145 | fullStats.Fastest = fullStats.Fastest == 0 ? watcher.ElapsedMilliseconds : Math.Min(fullStats.Fastest, watcher.ElapsedMilliseconds); 146 | fullStats.Slowest = Math.Max(fullStats.Slowest, watcher.ElapsedMilliseconds); 147 | fullStats.MaskedAPIKey = maskedKey; 148 | } 149 | #pragma warning disable IDE0018 // Inline variable declaration - Build.Bat Error Happens if We Do 150 | int maxRequestCount = 0; 151 | IEnumerable headers; 152 | IEnumerable requestRemains; 153 | if (response.Headers.TryGetValues("X-MaxRequestCount", out headers)) 154 | { 155 | // Get the rate-limit period ends at timestamp in seconds. 156 | maxRequestCount = Convert.ToInt32(headers.First()); 157 | } 158 | 159 | if (response.Headers.TryGetValues("X-AuthTokenExpiration", out headers)) 160 | { 161 | uint secondsToExpiration = 0; 162 | secondsToExpiration = Convert.ToUInt32(headers.First()); 163 | fullStats.Expired = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) 164 | .AddSeconds(secondsToExpiration).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss"); 165 | } 166 | 167 | if (response.Headers.TryGetValues("X-RateRequestsRemaining", out requestRemains)) 168 | { 169 | // Get the rate-limit period ends at timestamp in seconds. 170 | int requestRemain = Convert.ToInt32(requestRemains.First()); 171 | fullStats.HealthyRate = (double)(requestRemain) / maxRequestCount; 172 | UpdateRate(key, requestRemain); 173 | } 174 | APIConfiguration.Logger.HashStatusUpdate(fullStats); 175 | #pragma warning restore IDE0018 // Inline variable declaration - Build.Bat Error Happens if We Do 176 | } 177 | 178 | // TODO: Fix this up with proper retry-after when we get rate limited. 179 | switch (response.StatusCode) 180 | { 181 | // All good. Return the hashes back to the caller. :D 182 | case HttpStatusCode.OK: 183 | return JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); 184 | 185 | // Returned when something in your request is "invalid". Also when X-AuthToken is not set. 186 | // See the error message for why it is bad. 187 | case HttpStatusCode.BadRequest: 188 | string responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 189 | if (responseText.Contains("Unauthorized")) 190 | { 191 | APIConfiguration.Logger.LogDebug($"Unauthorized : {key} "); 192 | if (apiKeys.Count()> 1){ 193 | apiKeys.RemoveAll(x => x.Key == key); 194 | return await RequestHashesAsync(request).ConfigureAwait(false); 195 | } 196 | throw new HasherException($"Your API Key: {maskedKey} is incorrect or expired, please check auth.json (Pokefamer Message : {responseText})"); 197 | } 198 | Console.WriteLine($"Bad request sent to the hashing server! {responseText}"); 199 | 200 | break; 201 | 202 | // This error code is returned when your "key" is not in a valid state. (Expired, invalid, etc) 203 | case HttpStatusCode.Unauthorized: 204 | APIConfiguration.Logger.LogDebug($"Unauthorized : {key} "); 205 | if (apiKeys.Count()> 1){ 206 | apiKeys.RemoveAll(x => x.Key == key); 207 | return await RequestHashesAsync(request).ConfigureAwait(false); 208 | } 209 | 210 | throw new HasherException($"You are not authorized to use this service. Please check that your API key {maskedKey} is correct."); 211 | 212 | // This error code is returned when you have exhausted your current "hashes per second" value 213 | // You should queue up your requests, and retry in a second. 214 | case (HttpStatusCode)429: 215 | APIConfiguration.Logger.LogInfo($"Your request has been limited. {await response.Content.ReadAsStringAsync().ConfigureAwait(false)}"); 216 | if (apiKeys.Count()> 1){ 217 | return await RequestHashesAsync(request).ConfigureAwait(false); 218 | } 219 | 220 | long ratePeriodEndsAtTimestamp; 221 | IEnumerable ratePeriodEndHeaderValues; 222 | if (response.Headers.TryGetValues("X-RatePeriodEnd", out ratePeriodEndHeaderValues)) 223 | { 224 | // Get the rate-limit period ends at timestamp in seconds. 225 | ratePeriodEndsAtTimestamp = Convert.ToInt64(ratePeriodEndHeaderValues.First()); 226 | } 227 | else 228 | { 229 | // If for some reason we couldn't get the timestamp, just default to 2 second wait. 230 | ratePeriodEndsAtTimestamp = Utils.GetTime(false) + 2; 231 | } 232 | 233 | long timeToWaitInSeconds = ratePeriodEndsAtTimestamp - Utils.GetTime(false); 234 | 235 | if (timeToWaitInSeconds > 0) 236 | await Task.Delay((int)(timeToWaitInSeconds * 1000)).ConfigureAwait(false); // Wait until next rate-limit period begins. 237 | 238 | return await RequestHashesAsync(request).ConfigureAwait(false); 239 | default: 240 | throw new HasherException($"Hash API server ({client.BaseAddress}{apiEndPoint}) might down!"); 241 | } 242 | 243 | } 244 | 245 | return null; 246 | } 247 | 248 | private void UpdateRate(string key, int remain) 249 | { 250 | apiKeys.RemoveAll(x => x.Key == key); 251 | apiKeys.Add(new KeyValuePair(key, remain)); 252 | } 253 | private string GetAPIKey() 254 | { 255 | if (apiKeys == null) 256 | { 257 | apiKeys = new List>(); 258 | 259 | var allkeys = apiKey.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); 260 | foreach (var item in allkeys) 261 | { 262 | apiKeys.Add(new KeyValuePair(item, int.MaxValue)); 263 | } 264 | } 265 | apiKeys.OrderByDescending(x => x.Value); 266 | return apiKeys.First().Key; 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /Helpers/CommonRequest.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using Google.Protobuf; 4 | using Google.Protobuf.Collections; 5 | using POGOProtos.Enums; 6 | using POGOProtos.Networking.Envelopes; 7 | using POGOProtos.Networking.Platform.Responses; 8 | using POGOProtos.Networking.Requests; 9 | using POGOProtos.Networking.Requests.Messages; 10 | using POGOProtos.Networking.Responses; 11 | using PokemonGo.RocketAPI.Exceptions; 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | 16 | #endregion 17 | 18 | namespace PokemonGo.RocketAPI.Helpers 19 | { 20 | public class CommonRequest 21 | { 22 | public async static void ProcessGetInboxResponse(Client client, GetInboxResponse getInboxResponse) 23 | { 24 | var notifcation_count = getInboxResponse.Inbox.Notifications.Count(); 25 | 26 | switch (getInboxResponse.Result) 27 | { 28 | case GetInboxResponse.Types.Result.Unset: 29 | break; 30 | case GetInboxResponse.Types.Result.Failure: 31 | APIConfiguration.Logger.InboxStatusUpdate($"There was an error, viewing your notifications!", ConsoleColor.Red); 32 | break; 33 | case GetInboxResponse.Types.Result.Success: 34 | if (getInboxResponse.Inbox.Notifications.Count > 0) 35 | { 36 | APIConfiguration.Logger.InboxStatusUpdate($"We got {notifcation_count} new notification(s).", ConsoleColor.Magenta); 37 | RepeatedField notificationIDs = new RepeatedField(); 38 | RepeatedField categorieIDs = new RepeatedField(); 39 | RepeatedField createTimestampMsIDs = new RepeatedField(); 40 | 41 | foreach (var notification in getInboxResponse.Inbox.Notifications) 42 | { 43 | notificationIDs.Add(notification.NotificationId); 44 | createTimestampMsIDs.Add(notification.CreateTimestampMs); 45 | categorieIDs.Add(notification.Category); 46 | } 47 | 48 | NotificationState notificationState = NotificationState.Viewed; 49 | //await client.Misc.OptOutPushNotificationCategory(categorieIDs).ConfigureAwait(false); 50 | UpdateNotificationResponse updateNotificationResponse = await client.Misc.UpdateNotification(notificationIDs, createTimestampMsIDs, notificationState).ConfigureAwait(false); 51 | APIConfiguration.Logger.InboxStatusUpdate($"Notifications {updateNotificationResponse.State}.", ConsoleColor.Magenta); 52 | } 53 | break; 54 | } 55 | } 56 | 57 | public static Request GetDownloadRemoteConfigVersionMessageRequest(Client client) 58 | { 59 | var downloadRemoteConfigVersionMessage = new DownloadRemoteConfigVersionMessage 60 | { 61 | Platform = client.Platform, 62 | AppVersion = client.AppVersion 63 | }; 64 | return new Request 65 | { 66 | RequestType = RequestType.DownloadRemoteConfigVersion, 67 | RequestMessage = downloadRemoteConfigVersionMessage.ToByteString() 68 | }; 69 | } 70 | 71 | public static Request GetGetAssetDigestMessageRequest(Client client) 72 | { 73 | var getAssetDigestMessage = new GetAssetDigestMessage 74 | { 75 | Platform = client.Platform, 76 | AppVersion = client.AppVersion 77 | }; 78 | return new Request 79 | { 80 | RequestType = RequestType.GetAssetDigest, 81 | RequestMessage = getAssetDigestMessage.ToByteString() 82 | }; 83 | } 84 | 85 | public static Request GetDownloadSettingsMessageRequest(Client client) 86 | { 87 | var downloadSettingsMessage = new DownloadSettingsMessage 88 | { 89 | Hash = client.SettingsHash 90 | }; 91 | return new Request 92 | { 93 | RequestType = RequestType.DownloadSettings, 94 | RequestMessage = downloadSettingsMessage.ToByteString() 95 | }; 96 | } 97 | 98 | public static Request GetDefaultGetHoloInventoryMessage(Client client) 99 | { 100 | var getHoloInventoryMessage = new GetHoloInventoryMessage 101 | { 102 | LastTimestampMs = client.InventoryLastUpdateTimestamp 103 | }; 104 | return new Request 105 | { 106 | RequestType = RequestType.GetHoloInventory, 107 | RequestMessage = getHoloInventoryMessage.ToByteString() 108 | }; 109 | } 110 | 111 | public static Request[] AppendCheckChallenge(Request request) 112 | { 113 | return new[] 114 | { 115 | request, 116 | new Request 117 | { 118 | RequestType = RequestType.CheckChallenge, 119 | RequestMessage = new CheckChallengeMessage().ToByteString() 120 | } 121 | }; 122 | } 123 | 124 | public static Request GetVerifyChallenge(string token) 125 | { 126 | return new Request 127 | { 128 | RequestType = RequestType.VerifyChallenge, 129 | RequestMessage = new VerifyChallengeMessage() 130 | { 131 | Token = token, 132 | 133 | }.ToByteString() 134 | }; 135 | } 136 | 137 | public static List FillRequest(Request request, Client client, params RequestType[] excludes) 138 | { 139 | var requests = GetCommonRequests(client, excludes); 140 | requests.Insert(0, request); 141 | 142 | return requests; 143 | } 144 | 145 | public static List GetCommonRequests(Client client, params RequestType[] excludes) 146 | { 147 | List commonRequestsList = new List 148 | { 149 | new Request 150 | { 151 | RequestType = RequestType.CheckChallenge, 152 | RequestMessage = new CheckChallengeMessage().ToByteString() 153 | }, 154 | new Request 155 | { 156 | RequestType = RequestType.GetHatchedEggs, 157 | RequestMessage = new GetHatchedEggsMessage().ToByteString() 158 | }, 159 | GetDefaultGetHoloInventoryMessage(client), 160 | new Request 161 | { 162 | RequestType = RequestType.CheckAwardedBadges, 163 | RequestMessage = new CheckAwardedBadgesMessage().ToByteString() 164 | }, 165 | GetDownloadSettingsMessageRequest(client), 166 | new Request 167 | { 168 | RequestType = RequestType.GetBuddyWalked, 169 | RequestMessage = new GetBuddyWalkedMessage().ToByteString() 170 | } 171 | }; 172 | return commonRequestsList.Where(r => !excludes.Contains(r.RequestType)).ToList(); 173 | } 174 | 175 | public static void ProcessGetHoloInventoryResponse(Client client, GetHoloInventoryResponse getHoloInventoryResponse) 176 | { 177 | if (getHoloInventoryResponse == null) 178 | return; 179 | 180 | if (getHoloInventoryResponse.Success) 181 | { 182 | if (getHoloInventoryResponse.InventoryDelta == null) 183 | return; 184 | 185 | if (getHoloInventoryResponse.InventoryDelta.NewTimestampMs >= client.InventoryLastUpdateTimestamp) 186 | { 187 | client.InventoryLastUpdateTimestamp = getHoloInventoryResponse.InventoryDelta.NewTimestampMs; 188 | } 189 | 190 | client.Inventory.MergeWith(getHoloInventoryResponse); 191 | } 192 | } 193 | 194 | public static void ProcessDownloadSettingsResponse(Client client, DownloadSettingsResponse downloadSettingsResponse) 195 | { 196 | if (downloadSettingsResponse == null) 197 | return; 198 | 199 | if (string.IsNullOrEmpty(downloadSettingsResponse.Error)) 200 | { 201 | if (downloadSettingsResponse.Settings == null) 202 | return; 203 | 204 | client.SettingsHash = downloadSettingsResponse.Hash; 205 | client.GlobalSettings = downloadSettingsResponse.Settings; 206 | if (!string.IsNullOrEmpty(downloadSettingsResponse.Settings.MinimumClientVersion)) 207 | { 208 | client.MinimumClientVersion = new Version(downloadSettingsResponse.Settings.MinimumClientVersion); 209 | // ranamed to UseCustomAPI if (!client.Settings.UseLegacyAPI) 210 | //{ 211 | if (client.CheckCurrentVersionOutdated()) 212 | throw new MinimumClientVersionException(client.CurrentApiEmulationVersion, client.MinimumClientVersion); 213 | //} 214 | } 215 | } 216 | } 217 | 218 | public static void ProcessCheckChallengeResponse(Client client, CheckChallengeResponse checkChallengeResponse) 219 | { 220 | if (checkChallengeResponse == null) 221 | return; 222 | 223 | if (checkChallengeResponse.ShowChallenge) 224 | throw new CaptchaException(checkChallengeResponse.ChallengeUrl); 225 | } 226 | 227 | public static void ProcessGetPlayerResponse(Client client, GetPlayerResponse getPlayerResponse) 228 | { 229 | if (getPlayerResponse == null) 230 | return; 231 | 232 | if (getPlayerResponse.Banned) 233 | APIConfiguration.Logger.LogError("Error: This account seems be banned"); 234 | 235 | if (getPlayerResponse.Warn) 236 | APIConfiguration.Logger.LogInfo("Warning: This account seems be flagged, it's recommended to not bot on this account for now!"); 237 | 238 | if (getPlayerResponse.PlayerData != null) 239 | client.Player.PlayerData = getPlayerResponse.PlayerData; 240 | } 241 | 242 | public static void ProcessPlatform8Response(Client client, ResponseEnvelope responseEnvelope) 243 | { 244 | foreach (var platformReturn in responseEnvelope.PlatformReturns) 245 | { 246 | if (platformReturn.Type == POGOProtos.Networking.Platform.PlatformRequestType.UnknownPtr8) 247 | { 248 | var ptr8Response = new UnknownPtr8Response(); 249 | ptr8Response.MergeFrom(platformReturn.Response); 250 | client.UnknownPlat8Field = ptr8Response.Message; 251 | } 252 | } 253 | } 254 | } 255 | } -------------------------------------------------------------------------------- /Helpers/HashBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Helpers 4 | { 5 | public static class HashBuilder 6 | { 7 | /* IOS 1.15.0 */ 8 | static ulong[] magic_table = { 9 | 0x2dd7caaefcf073eb, 0xa9209937349cfe9c, 10 | 0xb84bfc934b0e60ef, 0xff709c157b26e477, 11 | 0x3936fd8735455112, 0xca141bf22338d331, 12 | 0xdd40e749cb64fd02, 0x5e268f564b0deb26, 13 | 0x658239596bdea9ec, 0x31cedf33ac38c624, 14 | 0x12f56816481b0cfd, 0x94e9de155f40f095, 15 | 0x5089c907844c6325, 0xdf887e97d73c50e3, 16 | 0xae8870787ce3c11d, 0xa6767d18c58d2117, 17 | }; 18 | 19 | static UInt128 ROUND_MAGIC = new UInt128(0xe3f0d44988bcdfab, 0x081570afdd535ec3); 20 | static ulong FINAL_MAGIC0 = 0xce7c4801d683e824; 21 | static ulong FINAL_MAGIC1 = 0x6823775b1daad522; 22 | static UInt32 HASH_SEED = 0x46e945f8; 23 | 24 | private static ulong Read_int64(byte[] p, int offset) { return BitConverter.ToUInt64(p, offset); } 25 | 26 | public static UInt32 Hash32(byte[] buffer) 27 | { 28 | return Hash32Salt(buffer, HASH_SEED); 29 | } 30 | 31 | public static UInt32 Hash32Salt(byte[] buffer, UInt32 salt) 32 | { 33 | var ret = Hash64Salt(buffer, salt); 34 | return (UInt32)ret ^ (UInt32)(ret >> 32); 35 | } 36 | 37 | public static UInt64 Hash64(byte[] buffer) 38 | { 39 | return Hash64Salt(buffer, HASH_SEED); 40 | } 41 | 42 | public static UInt64 Hash64Salt(byte[] buffer, UInt32 salt) 43 | { 44 | byte[] newBuffer = new byte[buffer.Length + 4]; 45 | byte[] saltBytes = BitConverter.GetBytes(salt); 46 | Array.Reverse(saltBytes); 47 | Array.Copy(saltBytes, 0, newBuffer, 0, saltBytes.Length); 48 | Array.Copy(buffer, 0, newBuffer, saltBytes.Length, buffer.Length); 49 | 50 | return ComputeHash(newBuffer); 51 | } 52 | 53 | public static UInt64 Hash64Salt64(byte[] buffer, UInt64 salt) 54 | { 55 | byte[] newBuffer = new byte[buffer.Length + 8]; 56 | byte[] saltBytes = BitConverter.GetBytes(salt); 57 | Array.Reverse(saltBytes); 58 | Array.Copy(saltBytes, 0, newBuffer, 0, saltBytes.Length); 59 | Array.Copy(buffer, 0, newBuffer, saltBytes.Length, buffer.Length); 60 | 61 | return ComputeHash(newBuffer); 62 | } 63 | 64 | public static ulong ComputeHash(byte[] input) 65 | { 66 | uint len = (uint)input.Length; 67 | uint num_chunks = len / 128; 68 | 69 | // copy tail, pad with zeroes 70 | byte[] tail = new byte[128]; 71 | uint tail_size = len % 128; 72 | Array.Copy(input, len - tail_size, tail, 0, tail_size); 73 | 74 | UInt128 hash; 75 | 76 | if (num_chunks != 0) hash = HashChunk(input, 128, 0); 77 | else hash = HashChunk(tail, tail_size, 0); 78 | 79 | hash += ROUND_MAGIC; 80 | 81 | int offset = 0; 82 | 83 | if (num_chunks != 0) 84 | { 85 | while (--num_chunks > 0) 86 | { 87 | offset += 128; 88 | hash = HashMulAdd(hash, ROUND_MAGIC, HashChunk(input, 128, offset)); 89 | } 90 | 91 | if (tail_size > 0) 92 | { 93 | hash = HashMulAdd(hash, ROUND_MAGIC, HashChunk(tail, tail_size, 0)); 94 | } 95 | } 96 | 97 | hash += new UInt128(tail_size * 8, 0); 98 | 99 | if (hash > new UInt128(0x7fffffffffffffff, 0xffffffffffffffff)) hash++; 100 | 101 | hash = hash << 1 >> 1; 102 | 103 | ulong X = hash.hi + (hash.lo >> 32); 104 | X = ((X + (X >> 32) + 1) >> 32) + hash.hi; 105 | ulong Y = (X << 32) + hash.lo; 106 | 107 | ulong A = X + FINAL_MAGIC0; 108 | if (A < X) A += 0x101; 109 | 110 | ulong B = Y + FINAL_MAGIC1; 111 | if (B < Y) B += 0x101; 112 | 113 | UInt128 H = new UInt128(A) * B; 114 | UInt128 mul = new UInt128(0x101); 115 | H = (mul * H.hi) + H.lo; 116 | H = (mul * H.hi) + H.lo; 117 | 118 | if (H.hi > 0) H += mul; 119 | if (H.lo > 0xFFFFFFFFFFFFFEFE) H += mul; 120 | return H.lo; 121 | } 122 | 123 | private static UInt128 HashChunk(byte[] chunk, long size, int off) 124 | { 125 | UInt128 hash = new UInt128(0); 126 | for (int i = 0; i < 8; i++) 127 | { 128 | int offset = i * 16; 129 | if (offset >= size) break; 130 | ulong a = Read_int64(chunk, off + offset); 131 | ulong b = Read_int64(chunk, off + offset + 8); 132 | hash += (new UInt128(a + magic_table[i * 2])) * (new UInt128(b + magic_table[i * 2 + 1])); 133 | } 134 | return hash << 2 >> 2; 135 | } 136 | 137 | private static UInt128 HashMulAdd(UInt128 hash, UInt128 mul, UInt128 add) 138 | { 139 | ulong a0 = add.lo & 0xffffffff, 140 | a1 = add.lo >> 32, 141 | a23 = add.hi; 142 | 143 | ulong m0 = mul.lo & 0xffffffff, 144 | m1 = mul.lo >> 32, 145 | m2 = mul.hi & 0xffffffff, 146 | m3 = mul.hi >> 32; 147 | 148 | ulong h0 = hash.lo & 0xffffffff, 149 | h1 = hash.lo >> 32, 150 | h2 = hash.hi & 0xffffffff, 151 | h3 = hash.hi >> 32; 152 | 153 | ulong c0 = (h0 * m0), 154 | c1 = (h0 * m1) + (h1 * m0), 155 | c2 = (h0 * m2) + (h1 * m1) + (h2 * m0), 156 | c3 = (h0 * m3) + (h1 * m2) + (h2 * m1) + (h3 * m0), 157 | c4 = (h1 * m3) + (h2 * m2) + (h3 * m1), 158 | c5 = (h2 * m3) + (h3 * m2), 159 | c6 = (h3 * m3); 160 | 161 | ulong r2 = c2 + (c6 << 1) + a23, 162 | r3 = c3 + (r2 >> 32), 163 | r0 = c0 + (c4 << 1) + a0 + (r3 >> 31), 164 | r1 = c1 + (c5 << 1) + a1 + (r0 >> 32); 165 | 166 | ulong res0 = ((r3 << 33 >> 1) | (r2 & 0xffffffff)) + (r1 >> 32); 167 | return new UInt128(res0, (r1 << 32) | (r0 & 0xffffffff)); 168 | } 169 | } 170 | 171 | struct UInt128 172 | { 173 | public ulong hi, lo; 174 | 175 | #region constructors 176 | 177 | public UInt128(ulong high, ulong low) 178 | { 179 | hi = high; lo = low; 180 | } 181 | 182 | public UInt128(ulong low) 183 | { 184 | hi = 0; lo = low; 185 | } 186 | 187 | #endregion 188 | #region comparators 189 | 190 | public bool Equals(UInt128 other) 191 | { 192 | return (hi == other.hi && lo == other.lo); 193 | } 194 | 195 | public static bool operator >(UInt128 a, UInt128 b) 196 | { 197 | if (a.hi == b.hi) return a.lo > b.lo; 198 | return a.hi > b.hi; 199 | } 200 | 201 | public static bool operator <(UInt128 a, UInt128 b) 202 | { 203 | if (a.hi == b.hi) return a.lo < b.lo; 204 | return a.hi < b.hi; 205 | } 206 | 207 | #endregion 208 | #region arithmetic 209 | 210 | public static UInt128 operator ++(UInt128 a) 211 | { 212 | a.lo++; 213 | if (a.lo == 0) a.hi++; 214 | return a; 215 | } 216 | 217 | public static UInt128 operator +(UInt128 a, UInt128 b) 218 | { 219 | ulong C = (((a.lo & b.lo) & 1) + (a.lo >> 1) + (b.lo >> 1)) >> 63; 220 | return new UInt128(a.hi + b.hi + C, a.lo + b.lo); 221 | } 222 | 223 | public static UInt128 operator +(UInt128 a, ulong b) 224 | { 225 | return a + new UInt128(b); 226 | } 227 | 228 | public static UInt128 operator -(UInt128 a, UInt128 b) 229 | { 230 | ulong L = a.lo - b.lo; 231 | ulong C = (((L & b.lo) & 1) + (b.lo >> 1) + (L >> 1)) >> 63; 232 | return new UInt128(a.hi - (b.hi + C), L); 233 | } 234 | 235 | #endregion 236 | #region bitwise operations 237 | 238 | public static UInt128 operator &(UInt128 a, UInt128 b) 239 | { 240 | return new UInt128(a.hi & b.hi, a.lo & b.lo); 241 | } 242 | 243 | public static UInt128 operator <<(UInt128 a, int b) 244 | { 245 | a.hi <<= b; 246 | a.hi |= (a.lo >> (64 - b)); 247 | a.lo <<= b; 248 | return a; 249 | } 250 | 251 | public static UInt128 operator >>(UInt128 a, int b) 252 | { 253 | a.lo >>= b; 254 | a.lo |= (a.hi << (64 - b)); 255 | a.hi >>= b; 256 | return a; 257 | } 258 | 259 | #endregion 260 | #region multiplication 261 | 262 | private static UInt128 M64(ulong a, ulong b) 263 | { 264 | ulong a1 = (a & 0xffffffff), b1 = (b & 0xffffffff), 265 | t = (a1 * b1), w3 = (t & 0xffffffff), k = (t >> 32), w1; 266 | 267 | a >>= 32; 268 | t = (a * b1) + k; 269 | k = (t & 0xffffffff); 270 | w1 = (t >> 32); 271 | 272 | b >>= 32; 273 | t = (a1 * b) + k; 274 | k = (t >> 32); 275 | 276 | return new UInt128((a * b) + w1 + k, (t << 32) + w3); 277 | } 278 | 279 | public static UInt128 operator *(UInt128 a, int b) { return a * (ulong)b; } 280 | 281 | public static UInt128 operator *(UInt128 a, ulong b) 282 | { 283 | UInt128 ans = M64(a.lo, b); 284 | ans.hi += (a.hi * b); 285 | return ans; 286 | } 287 | 288 | public static UInt128 operator *(UInt128 a, UInt128 b) 289 | { 290 | UInt128 ans = M64(a.lo, b.lo); 291 | ans.hi += (a.hi * b.lo) + (a.lo * b.hi); 292 | return ans; 293 | } 294 | 295 | #endregion 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /Helpers/HtmlRemoval.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by SharpDevelop. 3 | * User: Xelwon 4 | * Date: 11/03/2017 5 | * Time: 22:57 6 | * 7 | * To change this template use Tools | Options | Coding | Edit Standard Headers. 8 | */ 9 | using System.Text.RegularExpressions; 10 | 11 | namespace PokemonGo.RocketAPI.Helpers 12 | { 13 | /// 14 | /// Description of HtmlRemoval. 15 | /// 16 | public static class HtmlRemoval 17 | { 18 | /// 19 | /// Remove HTML from string with Regex. 20 | /// 21 | public static string StripTagsRegex(string source) 22 | { 23 | return Regex.Replace(source, "<.*?>", string.Empty); 24 | } 25 | 26 | /// 27 | /// Compiled regular expression for performance. 28 | /// 29 | static Regex _htmlRegex = new Regex("<.*?>", RegexOptions.Compiled); 30 | 31 | /// 32 | /// Remove HTML from string with compiled Regex. 33 | /// 34 | public static string StripTagsRegexCompiled(string source) 35 | { 36 | return _htmlRegex.Replace(source, string.Empty); 37 | } 38 | 39 | /// 40 | /// Remove HTML tags from string using char array. 41 | /// 42 | public static string StripTagsCharArray(string source) 43 | { 44 | var array = new char[source.Length]; 45 | int arrayIndex = 0; 46 | bool inside = false; 47 | 48 | for (int i = 0; i < source.Length; i++) { 49 | char let = source[i]; 50 | if (let == '<') { 51 | inside = true; 52 | continue; 53 | } 54 | if (let == '>') { 55 | inside = false; 56 | continue; 57 | } 58 | if (!inside) { 59 | array[arrayIndex] = let; 60 | arrayIndex++; 61 | } 62 | } 63 | return new string(array, 0, arrayIndex); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Helpers/HttpClientHelper.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Collections.Generic; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Threading.Tasks; 7 | 8 | #endregion 9 | 10 | namespace PokemonGo.RocketAPI.Helpers 11 | { 12 | public static class HttpClientHelper 13 | { 14 | public static async Task PostFormEncodedAsync(string url, 15 | params KeyValuePair[] keyValuePairs) 16 | 17 | { 18 | var handler = new HttpClientHandler 19 | { 20 | AutomaticDecompression = DecompressionMethods.GZip, 21 | AllowAutoRedirect = false, 22 | UseProxy = Client.Proxy != null, 23 | Proxy = Client.Proxy 24 | }; 25 | 26 | using (var tempHttpClient = new System.Net.Http.HttpClient(handler)) 27 | { 28 | var response = await tempHttpClient.PostAsync(url, new FormUrlEncodedContent(keyValuePairs)).ConfigureAwait(false); 29 | return await response.Content.ReadAsAsync().ConfigureAwait(false); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Helpers/JsonHelper.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using Newtonsoft.Json.Linq; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI.Helpers 8 | { 9 | public class JsonHelper 10 | { 11 | public static string GetValue(string json, string key) 12 | { 13 | var jObject = JObject.Parse(json); 14 | return jObject[key].ToString(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /Helpers/KillswitchTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace PokemonGo.RocketAPI.Helpers 6 | { 7 | public class KillSwitchTask 8 | { 9 | private CancellationTokenSource _killswitchCancellation; 10 | 11 | private Task _killswitchTask; 12 | 13 | private Client _client; 14 | 15 | internal KillSwitchTask(Client client) 16 | { 17 | _client = client; 18 | } 19 | 20 | /// 21 | /// Checks every once in a while if we need to activate killswitch. 22 | /// 23 | 24 | private async Task CheckKillSwitch(TaskCompletionSource firstCheckCompleted) 25 | { 26 | while (!_killswitchCancellation.IsCancellationRequested) 27 | { 28 | 29 | Version version = Client.GetMinimumRequiredVersionFromUrl(); 30 | if (version != null) 31 | _client.MinimumClientVersion = version; 32 | 33 | // after first check, signal as complete 34 | firstCheckCompleted?.TrySetResult(true); 35 | firstCheckCompleted = null; 36 | 37 | try 38 | { 39 | await Task.Delay(TimeSpan.FromSeconds(30), _killswitchCancellation.Token).ConfigureAwait(false); // Check every 30s 40 | } 41 | // cancelled 42 | catch (OperationCanceledException) 43 | { 44 | return; 45 | } 46 | } 47 | } 48 | 49 | internal async Task Start() 50 | { 51 | if (_killswitchTask != null) 52 | { 53 | // Task is already running, so just return. 54 | return; 55 | } 56 | 57 | var firstCheckCompleted = new TaskCompletionSource(); 58 | _killswitchCancellation = new CancellationTokenSource(); 59 | _killswitchTask = CheckKillSwitch(firstCheckCompleted); 60 | 61 | // wait for first check to complete 62 | await firstCheckCompleted.Task.ConfigureAwait(false); 63 | } 64 | 65 | internal void Stop() 66 | { 67 | _killswitchCancellation?.Cancel(); 68 | _killswitchTask = null; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Helpers/LehmerRng.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Helpers 4 | { 5 | public class LehmerRng 6 | { 7 | private const int a = 16807; 8 | private const int m = 2147483647; 9 | private const int q = 127773; 10 | private const int r = 2836; 11 | private int seed; 12 | public LehmerRng(int seed = 1) 13 | { 14 | if (seed <= 0 || seed == int.MaxValue) 15 | throw new Exception("Bad seed"); 16 | this.seed = seed; 17 | } 18 | 19 | public int Next() 20 | { 21 | int hi = seed / q; 22 | int lo = seed % q; 23 | int t = (a * lo) - (r * hi); 24 | if (t < 0) 25 | t += m; 26 | seed = t % unchecked((int)0x80000000); 27 | return seed; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Helpers/PokemonCpUtils.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf.Collections; 2 | using POGOProtos.Data; 3 | using POGOProtos.Enums; 4 | using POGOProtos.Settings.Master; 5 | using POGOProtos.Settings.Master.Pokemon; 6 | using System; 7 | using System.Collections.Generic; 8 | using static POGOProtos.Networking.Responses.DownloadItemTemplatesResponse.Types; 9 | 10 | namespace PokemonGo.RocketAPI.Helpers 11 | { 12 | public class PokemonCpUtils 13 | { 14 | private static Dictionary LEVEL_CP_MULTIPLIER = new Dictionary(); 15 | 16 | public static void Initialize(RepeatedField templates) 17 | { 18 | foreach (ItemTemplate template in templates) 19 | { 20 | if (template.PlayerLevel != null) 21 | { 22 | PlayerLevelSettings settings = template.PlayerLevel; 23 | RepeatedField multipliers = settings.CpMultiplier; 24 | for (int i = 0; i < multipliers.Count; i++) 25 | { 26 | double multiplier = multipliers[i]; 27 | LEVEL_CP_MULTIPLIER[i + 1.0F] = multiplier; 28 | double nextMultiplier = multipliers[Math.Min(multipliers.Count - 1, i + 1)]; 29 | double step = ((nextMultiplier * nextMultiplier) - (multiplier * multiplier)) / 2.0F; 30 | if (i >= 30) 31 | { 32 | step /= 2.0; 33 | } 34 | LEVEL_CP_MULTIPLIER[i + 1.5F] = Math.Sqrt((multiplier * multiplier) + step); 35 | } 36 | } 37 | } 38 | } 39 | 40 | public static float GetLevel(PokemonData pokemonData) 41 | { 42 | return GetLevelFromCpMultiplier(pokemonData.CpMultiplier + pokemonData.AdditionalCpMultiplier); 43 | } 44 | 45 | /** 46 | * Get the level from the cp multiplier 47 | * 48 | * @param combinedCpMultiplier All CP multiplier values combined 49 | * @return Level 50 | */ 51 | public static float GetLevelFromCpMultiplier(double combinedCpMultiplier) 52 | { 53 | double level; 54 | if (combinedCpMultiplier < 0.734f) 55 | { 56 | // compute polynomial approximation obtained by regression 57 | level = 58.35178527 * combinedCpMultiplier * combinedCpMultiplier 58 | - 2.838007664 * combinedCpMultiplier + 0.8539209906; 59 | } 60 | else 61 | { 62 | // compute linear approximation obtained by regression 63 | level = 171.0112688 * combinedCpMultiplier - 95.20425243; 64 | } 65 | // round to nearest .5 value and return 66 | return (float)(Math.Round((level) * 2) / 2.0); 67 | } 68 | 69 | /** 70 | * Get the maximum CP from the values 71 | * 72 | * @param attack All attack values combined 73 | * @param defense All defense values combined 74 | * @param stamina All stamina values combined 75 | * @return Maximum CP for these levels 76 | */ 77 | public static int GetMaxCp(int attack, int defense, int stamina) 78 | { 79 | return GetMaxCpForPlayer(attack, defense, stamina, 40); 80 | } 81 | 82 | /** 83 | * Get the absolute maximum CP for pokemons with their PokemonId. 84 | * 85 | * @param id The {@link PokemonIdOuterClass.PokemonId} of the Pokemon to get CP for. 86 | * @return The absolute maximum CP 87 | * @throws NoSuchItemException If the PokemonId value cannot be found in the {@link PokemonMeta}. 88 | */ 89 | public static int GetAbsoluteMaxCp(PokemonId id, int level = 40) 90 | { 91 | PokemonSettings settings = PokemonMeta.GetPokemonSettings(id); 92 | if (settings == null) { 93 | throw new Exception("Cannot find meta data for " + id); 94 | } 95 | StatsAttributes stats = settings.Stats; 96 | 97 | int attack = 15 + stats.BaseAttack; 98 | int defense = 15 + stats.BaseDefense; 99 | int stamina = 15 + stats.BaseStamina; 100 | return GetMaxCpForPlayer(attack, defense, stamina, level); 101 | } 102 | 103 | /** 104 | * Get the maximum CP from the values 105 | * 106 | * @param attack All attack values combined 107 | * @param defense All defense values combined 108 | * @param stamina All stamina values combined 109 | * @param playerLevel The player level 110 | * @return Maximum CP for these levels 111 | */ 112 | public static int GetMaxCpForPlayer(int attack, int defense, int stamina, int playerLevel) 113 | { 114 | float maxLevel = Math.Min(playerLevel + 1.5f, 40f); 115 | double maxCpMultplier = LEVEL_CP_MULTIPLIER[maxLevel]; 116 | return GetCp(attack, defense, stamina, maxCpMultplier); 117 | } 118 | 119 | 120 | public static int GetCp(PokemonData pokemon) 121 | { 122 | // Below is an example of how to calculate the CP manually. This should match pokemon.Cp. 123 | /* 124 | PokemonSettings settings = PokemonMeta.GetPokemonSettings(pokemon.PokemonId); 125 | if (settings == null) 126 | { 127 | throw new Exception("Cannot find meta data for " + pokemon.PokemonId); 128 | } 129 | StatsAttributes stats = settings.Stats; 130 | 131 | int attack = pokemon.IndividualAttack + stats.BaseAttack; 132 | int defense = pokemon.IndividualDefense + stats.BaseDefense; 133 | int stamina = pokemon.IndividualStamina + stats.BaseStamina; 134 | int cp = GetCp(attack, defense, stamina, pokemon.CpMultiplier + pokemon.AdditionalCpMultiplier); 135 | */ 136 | 137 | return pokemon.Cp; 138 | } 139 | 140 | /** 141 | * Calculate CP based on raw values 142 | * 143 | * @param attack All attack values combined 144 | * @param defense All defense values combined 145 | * @param stamina All stamina values combined 146 | * @param combinedCpMultiplier All CP multiplier values combined 147 | * @return CP 148 | */ 149 | public static int GetCp(int attack, int defense, int stamina, double combinedCpMultiplier) 150 | { 151 | return (int)Math.Round(attack * Math.Pow(defense, 0.5) * Math.Pow(stamina, 0.5) * Math.Pow(combinedCpMultiplier, 2) / 10f); 152 | } 153 | 154 | /** 155 | * Get the CP after powerup 156 | * 157 | * @param cp Current CP level 158 | * @param combinedCpMultiplier All CP multiplier values combined 159 | * @return New CP 160 | */ 161 | public static int GetCpAfterPowerup(int cp, double combinedCpMultiplier) 162 | { 163 | // Based on http://pokemongo.gamepress.gg/power-up-costs 164 | double level = GetLevelFromCpMultiplier(combinedCpMultiplier); 165 | if (level <= 10) 166 | { 167 | return cp + (int)Math.Round((cp * 0.009426125469) / Math.Pow(combinedCpMultiplier, 2)); 168 | } 169 | if (level <= 20) 170 | { 171 | return cp + (int)Math.Round((cp * 0.008919025675) / Math.Pow(combinedCpMultiplier, 2)); 172 | } 173 | if (level <= 30) 174 | { 175 | return cp + (int)Math.Round((cp * 0.008924905903) / Math.Pow(combinedCpMultiplier, 2)); 176 | } 177 | return cp + (int)Math.Round((cp * 0.00445946079) / Math.Pow(combinedCpMultiplier, 2)); 178 | } 179 | 180 | public static double GetAdditionalCpMultiplierAfterPowerup(double cpMultiplier, double additionalCpMultiplier) 181 | { 182 | float nextLevel = GetLevelFromCpMultiplier(cpMultiplier + additionalCpMultiplier) + .5f; 183 | return LEVEL_CP_MULTIPLIER[nextLevel] - cpMultiplier; 184 | } 185 | 186 | public static int GetStardustCostsForPowerup(double combinedCpMultiplier) 187 | { 188 | int level = (int)GetLevelFromCpMultiplier(combinedCpMultiplier); 189 | return PokemonMeta.UpgradeSettings.StardustCost[level]; 190 | } 191 | 192 | public static int GetCandyCostsForPowerup(double combinedCpMultiplier) 193 | { 194 | int level = (int)GetLevelFromCpMultiplier(combinedCpMultiplier); 195 | return PokemonMeta.UpgradeSettings.CandyCost[level]; 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Helpers/PokemonMeta.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf.Collections; 2 | using POGOProtos.Enums; 3 | using POGOProtos.Inventory.Item; 4 | using POGOProtos.Networking.Responses; 5 | using POGOProtos.Settings.Master; 6 | using System.Collections.Generic; 7 | using static POGOProtos.Networking.Responses.DownloadItemTemplatesResponse.Types; 8 | 9 | namespace PokemonGo.RocketAPI.Helpers 10 | { 11 | public class PokemonMeta 12 | { 13 | public static List templates = new List(); 14 | public static Dictionary PokemonSettings = new Dictionary(); 15 | public static Dictionary MoveSettings = new Dictionary(); 16 | public static Dictionary BadgeSettings = new Dictionary(); 17 | public static Dictionary ItemSettings = new Dictionary(); 18 | 19 | public static GymBattleSettings BattleSettings; 20 | public static PokemonUpgradeSettings UpgradeSettings; 21 | 22 | public static void Update(DownloadItemTemplatesResponse templatesResponse) 23 | { 24 | RepeatedField templates = templatesResponse.ItemTemplates; 25 | PokemonMeta.templates.Clear(); 26 | PokemonMeta.templates.AddRange(templates); 27 | foreach (ItemTemplate template in templates) 28 | { 29 | if (template.PokemonSettings != null) 30 | { 31 | PokemonSettings pokemonSettings = template.PokemonSettings; 32 | PokemonSettings[pokemonSettings.PokemonId] = pokemonSettings; 33 | } 34 | else if (template.MoveSettings != null) 35 | { 36 | MoveSettings moveSettings = template.MoveSettings; 37 | MoveSettings[moveSettings.MovementId] = moveSettings; 38 | } 39 | else if (template.BadgeSettings != null) 40 | { 41 | BadgeSettings badgeSettings = template.BadgeSettings; 42 | BadgeSettings[badgeSettings.BadgeType] = badgeSettings; 43 | } 44 | else if (template.ItemSettings != null) 45 | { 46 | ItemSettings itemSettings = template.ItemSettings; 47 | ItemSettings[itemSettings.ItemId] = itemSettings; 48 | } 49 | else if (template.BattleSettings != null) 50 | { 51 | BattleSettings = template.BattleSettings; 52 | } 53 | else if (template.PokemonUpgrades != null) 54 | { 55 | UpgradeSettings = template.PokemonUpgrades; 56 | } 57 | } 58 | //Evolutions.initialize(templates); 59 | PokemonCpUtils.Initialize(templates); 60 | } 61 | 62 | public static PokemonSettings GetPokemonSettings(PokemonId pokemon) 63 | { 64 | return PokemonSettings[pokemon]; 65 | } 66 | 67 | public static MoveSettings GetMoveSettings(PokemonMove move) 68 | { 69 | return MoveSettings[move]; 70 | } 71 | 72 | public static BadgeSettings GetBadgeSettings(BadgeType badge) 73 | { 74 | return BadgeSettings[badge]; 75 | } 76 | 77 | public static ItemSettings GetItemSettings(ItemId item) 78 | { 79 | return ItemSettings[item]; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Helpers/PokemonMoveMeta.cs: -------------------------------------------------------------------------------- 1 | using POGOProtos.Enums; 2 | 3 | // Ported to C# by BadChoicesZ 4 | // Code ported from Grover-c13 - https://github.com/Grover-c13/PokeGOAPI-Java/ 5 | 6 | namespace PokemonGo.RocketAPI.Helpers 7 | { 8 | public class PokemonMoveMeta 9 | { 10 | public PokemonMove Move; 11 | private PokemonType _type; 12 | private int _power; 13 | private int _accuracy; 14 | private double _critChance; 15 | private int _time; 16 | private int _energy; 17 | 18 | public int GetTime() 19 | { 20 | return _time; 21 | } 22 | public void SetTime(int time) 23 | { 24 | _time = time; 25 | } 26 | 27 | public PokemonMove GetMove() 28 | { 29 | return Move; 30 | } 31 | public void SetMove(PokemonMove move) 32 | { 33 | Move = move; 34 | } 35 | 36 | public int GetPower() 37 | { 38 | return _power; 39 | } 40 | public void SetPower(int power) 41 | { 42 | _power = power; 43 | } 44 | 45 | public int GetAccuracy() 46 | { 47 | return _accuracy; 48 | } 49 | 50 | public void SetAccuracy(int accuracy) 51 | { 52 | _accuracy = accuracy; 53 | } 54 | 55 | public double GetCritChance() 56 | { 57 | return _critChance; 58 | } 59 | public void SetCritChance(double critChance) 60 | { 61 | _critChance = critChance; 62 | } 63 | public int GetEnergy() 64 | { 65 | return _energy; 66 | } 67 | 68 | public void SetEnergy(int energy) 69 | { 70 | _energy = energy; 71 | } 72 | 73 | public void SetType(PokemonType type) 74 | { 75 | _type = type; 76 | } 77 | 78 | private new PokemonType GetType() 79 | { 80 | return _type; 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Helpers/RandomHelper.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | #endregion 7 | 8 | namespace PokemonGo.RocketAPI.Helpers 9 | { 10 | public static class RandomHelper 11 | { 12 | private static readonly Random _random = new Random(); 13 | 14 | public static long GetLongRandom(long min, long max) 15 | { 16 | var buf = new byte[8]; 17 | _random.NextBytes(buf); 18 | var longRand = BitConverter.ToInt64(buf, 0); 19 | 20 | return Math.Abs(longRand%(max - min)) + min; 21 | } 22 | 23 | public static void RandomSleep(int min, int max) 24 | { 25 | Task.Delay( (_random.Next(min, max))).Wait(); 26 | } 27 | 28 | public static void RandomSleep(int average) 29 | { 30 | RandomSleep(average-100, average+100); 31 | } 32 | 33 | public async static Task RandomDelay(int average) 34 | { 35 | await RandomDelay(average-100, average+100); 36 | } 37 | 38 | public async static Task RandomDelay(int min, int max) 39 | { 40 | await Task.Delay( (_random.Next(min, max))); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Helpers/RandomIdGenerator.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is free software: you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation, either version 3 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | * GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License 13 | * along with this program. If not, see . 14 | */ 15 | 16 | using System; 17 | 18 | namespace PokemonGo.RocketAPI.Helpers 19 | { 20 | public class RandomIdGenerator 21 | { 22 | /* 23 | * OLD code 24 | * 25 | * private static long MULTIPLIER = 16807; 26 | private static long MODULUS = 0x7FFFFFFF; 27 | 28 | private long rpcIdHigh = 1; 29 | private long rpcId = 2; 30 | 31 | /** 32 | * Generates next request id and increments count 33 | * @return the next request id 34 | */ 35 | /* 36 | public long Next() 37 | { 38 | rpcIdHigh = MULTIPLIER * rpcIdHigh % MODULUS; 39 | return rpcId++ | (rpcIdHigh << 32); 40 | } 41 | */ 42 | 43 | public static ulong LastRequestID { get; private set; } 44 | 45 | // Thanks to Noctem and Xelwon 46 | // Lehmer random number generator - https://en.wikipedia.org/wiki/Lehmer_random_number_generator 47 | 48 | ulong MersenePrime = 0x7FFFFFFF; // M = 2^31 -1 = 2,147,483,647 (Mersenne prime M31) 49 | ulong PrimeRoot = 0x41A7; // A = 16807 (a primitive root modulo M31) 50 | ulong Quotient = 0x1F31D; // Q = 127773 = M / A (to avoid overflow on A * seed) 51 | ulong Rest = 0xB14; // R = 2836 = M % A (to avoid overflow on A * seed) 52 | public static ulong Hi = 1; 53 | public static ulong Lo = 2; 54 | 55 | public RandomIdGenerator() 56 | { 57 | LastRequestID = (LastRequestID == 0) ? 1 : LastRequestID; 58 | } 59 | 60 | public ulong Last() 61 | { 62 | return LastRequestID; 63 | } 64 | 65 | // Old method to obtain the request ID 66 | public ulong NextLehmerRandom() 67 | { 68 | Hi = 0; 69 | Lo = 0; 70 | ulong NewRequestID; 71 | 72 | Hi = LastRequestID / Quotient; 73 | Lo = LastRequestID % Quotient; 74 | 75 | NewRequestID = PrimeRoot * Lo - Rest * Hi; 76 | if (NewRequestID <= 0) 77 | NewRequestID = NewRequestID + MersenePrime; 78 | 79 | //Logger.Debug($"[OLD LEHMER] {NewRequestID.ToString("X")} [{Hi.ToString("X")},{Lo.ToString("X")}]"); 80 | 81 | NewRequestID = NewRequestID % 0x80000000; 82 | LastRequestID = NewRequestID; 83 | 84 | return NewRequestID; 85 | } 86 | 87 | // New method to obtain the request ID (extracted from pgoapi) 88 | // TODO: Check this with pgoapi. This has not sense for me (Xelwon) . 89 | // https://github.com/pogodevorg/pgoapi/blob/develop/pgoapi/rpc_api.py 90 | // Line 82 91 | public ulong NextSinceAPI0691() 92 | { 93 | Hi = PrimeRoot * Hi % MersenePrime; 94 | ulong NewRequestID = Lo++ | (Hi << 32); 95 | LastRequestID = NewRequestID; 96 | //Logger.Debug($"[NEW METHOD] {NewRequestID.ToString("X")} [{Hi.ToString("X")},{Lo.ToString("X")}]"); 97 | 98 | return NewRequestID; 99 | } 100 | 101 | public ulong Next() 102 | { 103 | return NextSinceAPI0691(); 104 | } 105 | } 106 | 107 | public class Uk27IdGenerator 108 | { 109 | private static int min = 1000; 110 | private static int max = 60000; 111 | private static readonly Random _random = new Random(); 112 | 113 | public void Init(int _min, int _max) 114 | { 115 | min = _min; 116 | max = _max; 117 | } 118 | 119 | public int Next() 120 | { 121 | return _random.Next(min, max); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Helpers/RetryHandler.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Diagnostics; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | #endregion 11 | 12 | namespace PokemonGo.RocketAPI.Helpers 13 | { 14 | internal class RetryHandler : DelegatingHandler 15 | { 16 | private const int MaxRetries = 25; 17 | 18 | public RetryHandler(HttpMessageHandler innerHandler) 19 | : base(innerHandler) 20 | { 21 | } 22 | 23 | protected override async Task SendAsync( 24 | HttpRequestMessage request, 25 | CancellationToken cancellationToken) 26 | { 27 | for (var i = 0; i <= MaxRetries; i++) 28 | { 29 | try 30 | { 31 | var response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false); 32 | if (response.StatusCode == HttpStatusCode.BadGateway || 33 | response.StatusCode == HttpStatusCode.InternalServerError) 34 | throw new Exception(); //todo: proper implementation 35 | 36 | return response; 37 | } 38 | catch (Exception ex) 39 | { 40 | Debug.WriteLine($"[#{i} of {MaxRetries}] retry request {request.RequestUri} - Error: {ex}"); 41 | if (i < MaxRetries) 42 | { 43 | await Task.Delay(1000, cancellationToken).ConfigureAwait(false); 44 | continue; 45 | } 46 | throw; 47 | } 48 | } 49 | return null; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Helpers/S2Helper.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Google.Common.Geometry; 6 | 7 | #endregion 8 | 9 | namespace PokemonGo.RocketAPI.Helpers 10 | { 11 | public static class S2Helper 12 | { 13 | public static List GetNearbyCellIds(double longitude, double latitude) 14 | { 15 | var nearbyCellIds = new List(); 16 | 17 | var cellId = S2CellId.FromLatLng(S2LatLng.FromDegrees(latitude, longitude)).ParentForLevel(15); 18 | 19 | nearbyCellIds.Add(cellId); 20 | 21 | var neighbours = new List(); 22 | cellId.GetAllNeighbors(15, neighbours); 23 | 24 | foreach (var neighbour in neighbours) 25 | { 26 | nearbyCellIds.Add(neighbour); 27 | nearbyCellIds.AddRange(neighbour.GetEdgeNeighbors()); 28 | } 29 | 30 | return nearbyCellIds.Select(c => c.Id).Distinct().OrderBy(c => c).ToList(); 31 | } 32 | 33 | private static S2CellId GetPrevious(S2CellId cellId, int depth) 34 | { 35 | if (depth < 0) 36 | return cellId; 37 | 38 | depth--; 39 | 40 | return GetPrevious(cellId.Previous, depth); 41 | } 42 | 43 | private static S2CellId GetNext(S2CellId cellId, int depth) 44 | { 45 | if (depth < 0) 46 | return cellId; 47 | 48 | depth--; 49 | 50 | return GetNext(cellId.Next, depth); 51 | } 52 | public static double[] GetLatLng(ulong cellid) 53 | { 54 | var s2cell = new S2CellId(cellid); 55 | return new []{s2cell.ToLatLng().LatDegrees,s2cell.ToLatLng().LngDegrees}; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Helpers/TimeUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace PokemonGo.RocketAPI.Util 4 | { 5 | public static class TimeUtil 6 | { 7 | private static DateTime _posixTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); 8 | 9 | /// 10 | /// Returns the current unix timestamp in milliseconds (UTC). 11 | /// 12 | /// 13 | public static long GetCurrentTimestampInMilliseconds() 14 | { 15 | return (long) (DateTime.UtcNow - _posixTime).TotalMilliseconds; 16 | } 17 | 18 | /// 19 | /// Returns the current unix timestamp in seconds (UTC). 20 | /// 21 | /// 22 | public static long GetCurrentTimestampInSeconds() 23 | { 24 | return (long) (DateTime.UtcNow - _posixTime).TotalSeconds; 25 | } 26 | 27 | public static DateTime GetDateTimeFromMilliseconds(long timestampMilliseconds) 28 | { 29 | return _posixTime.AddMilliseconds(timestampMilliseconds); 30 | } 31 | 32 | public static DateTime GetDateTimeFromSeconds(int timestampSeconds) 33 | { 34 | return _posixTime.AddSeconds(timestampSeconds); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Helpers/Utils.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Linq; 5 | 6 | #endregion 7 | 8 | namespace PokemonGo.RocketAPI.Helpers 9 | { 10 | public static class Utils 11 | { 12 | public static ulong FloatAsUlong(double value) 13 | { 14 | var bytes = BitConverter.GetBytes(value); 15 | return BitConverter.ToUInt64(bytes, 0); 16 | } 17 | 18 | public static long GetTime(bool ms = false) 19 | { 20 | var timeSpan = DateTime.UtcNow - new DateTime(1970, 1, 1); 21 | 22 | return ms ? (long)Math.Round(timeSpan.TotalMilliseconds) : (long)Math.Round(timeSpan.TotalSeconds); 23 | } 24 | 25 | public static uint GenerateLocation1(byte[] authTicket, double lat, double lng, double alt) 26 | { 27 | byte[] locationBytes = BitConverter.GetBytes(lat).Reverse() 28 | .Concat(BitConverter.GetBytes(lng).Reverse()) 29 | .Concat(BitConverter.GetBytes(alt).Reverse()).ToArray(); 30 | 31 | return HashBuilder.Hash32Salt(locationBytes, HashBuilder.Hash32(authTicket)); 32 | } 33 | 34 | public static uint GenerateLocation2(double lat, double lng, double alt) 35 | { 36 | byte[] locationBytes = BitConverter.GetBytes(lat).Reverse() 37 | .Concat(BitConverter.GetBytes(lng).Reverse()) 38 | .Concat(BitConverter.GetBytes(alt).Reverse()).ToArray(); 39 | return HashBuilder.Hash32(locationBytes); 40 | } 41 | 42 | public static ulong GenerateRequestHash(byte[] authTicket, byte[] hashRequest) 43 | { 44 | ulong seed = HashBuilder.Hash64(authTicket); 45 | return HashBuilder.Hash64Salt64(hashRequest, seed); 46 | } 47 | public static ulong CastToUnsigned(long number) 48 | { 49 | return (ulong)number; 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /HttpClient/PokemonClient.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Net; 4 | using System.Net.Http; 5 | using PokemonGo.RocketAPI.Helpers; 6 | 7 | #endregion 8 | 9 | namespace PokemonGo.RocketAPI.HttpClient 10 | { 11 | public class PokemonHttpClient : System.Net.Http.HttpClient 12 | { 13 | private static readonly HttpClientHandler Handler = new HttpClientHandler 14 | { 15 | AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, 16 | AllowAutoRedirect = false, 17 | UseProxy = Client.Proxy != null, 18 | Proxy = Client.Proxy 19 | }; 20 | 21 | public PokemonHttpClient() : base(new RetryHandler(Handler)) 22 | { 23 | DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Niantic App"); 24 | DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive"); 25 | DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*"); 26 | //DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded"); 27 | DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/binary"); 28 | DefaultRequestHeaders.TryAddWithoutValidation("Accept-Encoding", "identity, gzip"); 29 | DefaultRequestHeaders.ExpectContinue = false; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /ICaptchaResolver.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI 2 | { 3 | public interface ICaptchaResolver 4 | { 5 | void OnCaptcha(string captchaUrl); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ILogger.cs: -------------------------------------------------------------------------------- 1 | using PokemonGo.RocketAPI.Logging; 2 | using System; 3 | 4 | namespace PokemonGo.RocketAPI 5 | { 6 | public interface ILogger 7 | { 8 | void LogInfo(string message); 9 | void LogDebug(string message); 10 | void LogCritical(string message, dynamic data); 11 | void HashStatusUpdate(HashInfo info); 12 | void LogError(string message); 13 | void LogFlaggedInit(string message); 14 | void LogErrorInit(string message); 15 | void InboxStatusUpdate(string message, ConsoleColor color = ConsoleColor.White); 16 | } 17 | 18 | public class DefaultConsoleLogger : ILogger 19 | { 20 | public void InboxStatusUpdate(string message, ConsoleColor color = ConsoleColor.White) 21 | { 22 | Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] (INBOX) {message}", color); 23 | } 24 | 25 | public void HashStatusUpdate(HashInfo info) 26 | { 27 | Console.WriteLine($"[{DateTime.Now.ToString("HH:mm:ss")}] (HASH SERVER) [{info.MaskedAPIKey}] in last 1 minute {info.Last60MinAPICalles} request/min , AVG: {info.Last60MinAPIAvgTime:0.00} ms/request , Fastest : {info.Fastest}, Slowest: {info.Slowest}"); 28 | } 29 | 30 | public void LogCritical(string message, dynamic data) 31 | { 32 | Console.WriteLine("ERROR - CRITICAL " + message); 33 | } 34 | 35 | public void LogDebug(string message) 36 | { 37 | Console.WriteLine("Debug : " + message); 38 | } 39 | 40 | public void LogError(string message) 41 | { 42 | Console.WriteLine(message); 43 | } 44 | 45 | public void LogInfo(string message) 46 | { 47 | Console.WriteLine(message); 48 | } 49 | 50 | public void LogFlaggedInit(string message) 51 | { 52 | Console.WriteLine(message); 53 | } 54 | 55 | public void LogErrorInit(string message) 56 | { 57 | Console.WriteLine(message); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /ISettings.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using PokemonGo.RocketAPI.Enums; 4 | 5 | #endregion 6 | 7 | namespace PokemonGo.RocketAPI 8 | { 9 | public interface ISettings 10 | { 11 | AuthType AuthType { get; set; } 12 | double DefaultLatitude { get; set; } 13 | double DefaultLongitude { get; set; } 14 | double DefaultAltitude { get; set; } 15 | string GoogleRefreshToken { get; set; } 16 | string Password { get; set; } 17 | string Username { get; set; } 18 | string DevicePlatform { get; set; } 19 | string DeviceId { get; set; } 20 | string AndroidBoardName { get; set; } 21 | string AndroidBootloader { get; set; } 22 | string DeviceBrand { get; set; } 23 | string DeviceModel { get; set; } 24 | string DeviceModelIdentifier { get; set; } 25 | string DeviceModelBoot { get; set; } 26 | string HardwareManufacturer { get; set; } 27 | string HardwareModel { get; set; } 28 | string FirmwareBrand { get; set; } 29 | string FirmwareTags { get; set; } 30 | string FirmwareType { get; set; } 31 | string FirmwareFingerprint { get; set; } 32 | bool UseProxy { get; set; } 33 | bool UseProxyAuthentication { get; set; } 34 | string UseProxyHost { get; set; } 35 | string UseProxyPort { get; set; } 36 | string UseProxyUsername { get; set; } 37 | string UseProxyPassword { get; set; } 38 | bool UsePogoDevHashServer { get; set; } 39 | string UrlHashServices { get; set; } 40 | string EndPoint { get; set; } 41 | bool UseCustomAPI { get; set; } 42 | string AuthAPIKey { get; set; } 43 | bool DisplayVerboseLog { get; set; } 44 | bool AutoExitBotIfAccountFlagged { get; set; } 45 | double AccountLatitude { get; set; } 46 | double AccountLongitude { get; set; } 47 | bool AccountActive { get; set; } 48 | string Country { get; set; } 49 | string Language { get; set; } 50 | string TimeZone { get; set; } 51 | string POSIX { get; set; } 52 | double RunStart { get; set; } 53 | double RunEnd { get; set; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Logging/HashInfo.cs: -------------------------------------------------------------------------------- 1 | namespace PokemonGo.RocketAPI.Logging 2 | { 3 | public class HashInfo 4 | { 5 | public string Version { get; set; } 6 | public string Url { get; set; } 7 | 8 | public int APICalles { get; set; } 9 | 10 | public double TotalTimes { get; set; } 11 | public double Slowest { get; set; } 12 | public double Fastest { get; set; } 13 | 14 | public double Last60MinAPICalles { get; set; } 15 | public double Last60MinAPIAvgTime { get; set; } 16 | public string Expired { get; set; } 17 | public string MaskedAPIKey { get; set; } 18 | public double HealthyRate { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LoginProviders/GoogleLoginProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using GPSOAuthSharp; 4 | using PokemonGo.RocketAPI.Util; 5 | using PokemonGo.RocketAPI.Authentication.Data; 6 | 7 | namespace PokemonGo.RocketAPI.LoginProviders 8 | { 9 | /// 10 | /// The for Google. 11 | /// Use this if you want to authenticate to PokemonGo using a Google account. 12 | /// 13 | public class GoogleLoginProvider : ILoginProvider 14 | { 15 | private readonly string _username; 16 | private readonly string _password; 17 | 18 | public GoogleLoginProvider(string username, string password) 19 | { 20 | _username = username; 21 | _password = password; 22 | } 23 | 24 | /// 25 | /// The unique identifier of the . 26 | /// 27 | public string ProviderId => "google"; 28 | 29 | /// 30 | /// The unique identifier of the user trying to authenticate using the . 31 | /// 32 | public string UserId => _username; 33 | 34 | /// 35 | /// Retrieves an by logging into through the Google Play Services OAuth. 36 | /// 37 | /// Returns an . 38 | public async Task GetAccessToken() 39 | { 40 | var googleClient = new GPSOAuthClient(_username, _password); 41 | var masterLoginResponse = await googleClient.PerformMasterLogin().ConfigureAwait(false); 42 | 43 | if (masterLoginResponse.ContainsKey("Error")) 44 | { 45 | if (masterLoginResponse["Error"].Equals("NeedsBrowser")) 46 | throw new Exception($"You have to log into an browser with the email '{_username}'."); 47 | 48 | throw new Exception($"Google returned an error message: '{masterLoginResponse["Error"]}'"); 49 | } 50 | if (!masterLoginResponse.ContainsKey("Token")) 51 | { 52 | throw new Exception("Token was missing from master login response."); 53 | } 54 | var oauthResponse = await googleClient.PerformOAuth(masterLoginResponse["Token"], Constants.GoogleAuthService, 55 | Constants.GoogleAuthApp, Constants.GoogleAuthClientSig).ConfigureAwait(false); 56 | if (!oauthResponse.ContainsKey("Auth")) 57 | { 58 | throw new Exception("Auth token was missing from oauth login response."); 59 | } 60 | //Logger.Debug("Authenticated through Google."); 61 | return new AccessToken 62 | { 63 | Username = _username, 64 | Token = oauthResponse["Auth"], 65 | Expiry = TimeUtil.GetDateTimeFromSeconds(int.Parse(oauthResponse["Expiry"])), 66 | ProviderID = ProviderId 67 | }; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /LoginProviders/ILoginProvider.cs: -------------------------------------------------------------------------------- 1 | using PokemonGo.RocketAPI.Authentication.Data; 2 | using System.Threading.Tasks; 3 | 4 | namespace PokemonGo.RocketAPI.LoginProviders 5 | { 6 | public interface ILoginProvider 7 | { 8 | /// 9 | /// The unique identifier of this . 10 | /// 11 | string ProviderId { get; } 12 | 13 | /// 14 | /// Only used for informational purposes. Could be an email, username, etc. 15 | /// 16 | string UserId { get; } 17 | 18 | /// 19 | /// The method to obtain an using this . 20 | /// 21 | /// Returns an . 22 | Task GetAccessToken(); 23 | } 24 | } -------------------------------------------------------------------------------- /LoginProviders/PtcLoginProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Text.RegularExpressions; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using Newtonsoft.Json.Linq; 8 | using PokemonGo.RocketAPI.Authentication.Data; 9 | using System.Net; 10 | 11 | namespace PokemonGo.RocketAPI.LoginProviders 12 | { 13 | /// 14 | /// The for Pokemon Trainer Club. 15 | /// Use this if you want to authenticate to PokemonGo using a Pokemon Trainer Club account. 16 | /// 17 | public class PtcLoginProvider : ILoginProvider 18 | { 19 | private readonly string _username; 20 | private readonly string _password; 21 | 22 | public PtcLoginProvider(string username, string password) 23 | { 24 | _username = username; 25 | _password = password; 26 | } 27 | 28 | /// 29 | /// The unique identifier of the . 30 | /// 31 | public string ProviderId => "ptc"; 32 | 33 | /// 34 | /// The unique identifier of the user trying to authenticate using the . 35 | /// 36 | public string UserId => _username; 37 | 38 | /// 39 | /// Retrieves an by logging into the Pokemon Trainer Club website. 40 | /// 41 | /// Returns an . 42 | public async Task GetAccessToken() 43 | { 44 | using (var httpClientHandler = new HttpClientHandler()) 45 | { 46 | httpClientHandler.AllowAutoRedirect = false; 47 | httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip; 48 | httpClientHandler.UseProxy = Client.Proxy != null; 49 | httpClientHandler.Proxy = Client.Proxy; 50 | 51 | using (var httpClient = new System.Net.Http.HttpClient(httpClientHandler)) 52 | { 53 | httpClient.DefaultRequestHeaders.Accept.TryParseAdd(Constants.Accept); 54 | httpClient.DefaultRequestHeaders.Host = Constants.LoginHostValue; 55 | httpClient.DefaultRequestHeaders.Connection.TryParseAdd(Constants.Connection); 56 | httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd(Constants.LoginUserAgent); 57 | httpClient.DefaultRequestHeaders.AcceptLanguage.TryParseAdd(Constants.AcceptLanguage); 58 | httpClient.DefaultRequestHeaders.AcceptEncoding.TryParseAdd(Constants.AcceptEncoding); 59 | httpClient.DefaultRequestHeaders.TryAddWithoutValidation(Constants.LoginManufactor, Constants.LoginManufactorVersion); 60 | httpClient.Timeout.Add(Constants.TimeOut); 61 | LoginData loginData = await GetLoginData(httpClient).ConfigureAwait(false); 62 | var ticket = await PostLogin(httpClient, _username, _password, loginData).ConfigureAwait(false); 63 | var accessToken = await PostLoginOauth(httpClient, ticket).ConfigureAwait(false); 64 | accessToken.Username = _username; 65 | //Logger.Debug("Authenticated through PTC."); 66 | return accessToken; 67 | } 68 | } 69 | } 70 | 71 | /// 72 | /// Responsible for retrieving login parameters for . 73 | /// 74 | /// An initialized . 75 | /// for . 76 | private async Task GetLoginData(System.Net.Http.HttpClient httpClient) 77 | { 78 | var loginDataResponse = await httpClient.GetAsync(Constants.LoginUrl).ConfigureAwait(false); 79 | if (!loginDataResponse.IsSuccessStatusCode) 80 | throw new Exception($"Unexpected response from Pokemon SSO OAuth Login Url: Status code {loginDataResponse.StatusCode}"); 81 | 82 | var jsonData = await loginDataResponse.Content.ReadAsStringAsync().ConfigureAwait(false); 83 | var loginData = JsonConvert.DeserializeObject(jsonData); 84 | return loginData; 85 | } 86 | 87 | /// 88 | /// Responsible for submitting the login request. 89 | /// 90 | /// 91 | /// The user's PTC username. 92 | /// The user's PTC password. 93 | /// taken from PTC website using . 94 | /// 95 | private async Task PostLogin(System.Net.Http.HttpClient httpClient, string username, string password, LoginData loginData) 96 | { 97 | var loginResponse = 98 | await httpClient.PostAsync(Constants.LoginUrl, new FormUrlEncodedContent(new Dictionary 99 | { 100 | {"lt", loginData.Lt}, 101 | {"execution", loginData.Execution}, 102 | {"_eventId", "submit"}, 103 | {"username", username}, 104 | {"password", password} 105 | })).ConfigureAwait(false); 106 | 107 | var loginResponseDataRaw = await loginResponse.Content.ReadAsStringAsync().ConfigureAwait(false); 108 | if (!loginResponseDataRaw.Contains("{")) 109 | { 110 | var locationQuery = loginResponse.Headers.Location.Query; 111 | var ticketStartPosition = locationQuery.IndexOf("=", StringComparison.Ordinal) + 1; 112 | return locationQuery.Substring(ticketStartPosition, locationQuery.Length - ticketStartPosition); 113 | } 114 | 115 | var loginResponseData = JObject.Parse(loginResponseDataRaw); 116 | var loginResponseErrors = (JArray)loginResponseData["errors"]; 117 | 118 | throw new Exception($"Pokemon Trainer Club gave error(s): '{string.Join(",", loginResponseErrors)}'"); 119 | } 120 | 121 | /// 122 | /// Responsible for finishing the oauth login request. 123 | /// 124 | /// 125 | /// 126 | /// 127 | private async Task PostLoginOauth(System.Net.Http.HttpClient httpClient, string ticket) 128 | { 129 | var loginResponse = 130 | await httpClient.PostAsync(Constants.LoginOauthUrl, new FormUrlEncodedContent(new Dictionary 131 | { 132 | {"client_id", Constants.Client_Id}, 133 | {"redirect_uri", Constants.Redirect_Uri}, 134 | {"client_secret", Constants.Client_Secret}, 135 | {"grant_type", Constants.Grant_Type}, 136 | {"code", ticket} 137 | })).ConfigureAwait(false); 138 | 139 | var loginResponseDataRaw = await loginResponse.Content.ReadAsStringAsync().ConfigureAwait(false); 140 | 141 | var oAuthData = Regex.Match(loginResponseDataRaw, "access_token=(?.*?)&expires=(?\\d+)"); 142 | if (!oAuthData.Success) 143 | throw new Exception($"Couldn't verify the OAuth login response data '{loginResponseDataRaw}'."); 144 | 145 | return new AccessToken 146 | { 147 | Token = oAuthData.Groups["accessToken"].Value, 148 | Expiry = DateTime.UtcNow.AddSeconds(int.Parse(oAuthData.Groups["expires"].Value)), 149 | ProviderID = ProviderId 150 | }; 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /PokemonGo.RocketAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {05D2DA44-1B8E-4CF7-94ED-4D52451CD095} 8 | Library 9 | Properties 10 | PokemonGo.RocketAPI 11 | Pokemon.Go.Rocket.API 12 | v4.6.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | none 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | AnyCPU 35 | 36 | 37 | true 38 | bin\x86\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x86 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | false 45 | 46 | 47 | bin\x86\Release\ 48 | TRACE 49 | true 50 | none 51 | x86 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | false 55 | 56 | 57 | x64 58 | bin\x64\Debug\ 59 | TRACE;DEBUG 60 | false 61 | full 62 | 63 | 64 | x64 65 | bin\x64\Release\ 66 | TRACE 67 | false 68 | true 69 | 70 | 71 | 72 | $(SolutionDir)\packages\Portable.BouncyCastle.1.8.1.4\lib\net40\BouncyCastle.Crypto.dll 73 | 74 | 75 | $(SolutionDir)\packages\GeoCoordinate.NetStandard1.1.0.1\lib\netstandard1.1\GeoCoordinate.NetStandard1.dll 76 | 77 | 78 | $(SolutionDir)\packages\Google.Protobuf.3.5.1\lib\net45\Google.Protobuf.dll 79 | 80 | 81 | $(SolutionDir)\packages\GPSOAuthSharp.Core.1.0.3\lib\netstandard1.1\GPSOAuthSharp.Core.dll 82 | 83 | 84 | $(SolutionDir)\packages\Microsoft.Extensions.Logging.Abstractions.1.1.2\lib\netstandard1.1\Microsoft.Extensions.Logging.Abstractions.dll 85 | 86 | 87 | $(SolutionDir)\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll 88 | 89 | 90 | $(SolutionDir)\packages\POGOProtos.Core.2.24.0\lib\net45\POGOProtos.Core.dll 91 | 92 | 93 | $(SolutionDir)\packages\Thrower.4.3.1\lib\net461\PommaLabs.Thrower.dll 94 | 95 | 96 | $(SolutionDir)\packages\S2Geometry.1.0.3\lib\portable-net45+wp8+win8\S2Geometry.dll 97 | True 98 | 99 | 100 | 101 | $(SolutionDir)\packages\System.ComponentModel.Annotations.4.4.1\lib\net461\System.ComponentModel.Annotations.dll 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | $(SolutionDir)\packages\Microsoft.AspNet.WebApi.Client.5.2.4\lib\net45\System.Net.Http.Formatting.dll 111 | 112 | 113 | 114 | $(SolutionDir)\packages\System.Reflection.TypeExtensions.4.4.0\lib\net461\System.Reflection.TypeExtensions.dll 115 | 116 | 117 | 118 | $(SolutionDir)\packages\System.Security.Cryptography.Algorithms.4.3.1\lib\net461\System.Security.Cryptography.Algorithms.dll 119 | True 120 | 121 | 122 | $(SolutionDir)\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll 123 | True 124 | 125 | 126 | $(SolutionDir)\packages\System.Text.Encodings.Web.4.4.0\lib\netstandard2.0\System.Text.Encodings.Web.dll 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | $(SolutionDir)\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll 138 | True 139 | 140 | 141 | $(SolutionDir)\packages\Troschuetz.Random.4.3.0\lib\net461\Troschuetz.Random.dll 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | Designer 221 | 222 | 223 | 224 | 231 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | 6 | #endregion 7 | 8 | // General Information about an assembly is controlled through the following 9 | // set of attributes. Change these attribute values to modify the information 10 | // associated with an assembly. 11 | 12 | [assembly: AssemblyTitle("Pokémon Go Rocket API")] 13 | [assembly: AssemblyDescription("")] 14 | [assembly: AssemblyConfiguration("")] 15 | [assembly: AssemblyCompany("")] 16 | [assembly: AssemblyProduct("PokemonGo.RocketAPI")] 17 | [assembly: AssemblyCopyright("Copyright © 2016")] 18 | [assembly: AssemblyTrademark("")] 19 | [assembly: AssemblyCulture("")] 20 | 21 | // Setting ComVisible to false makes the types in this assembly not visible 22 | // to COM components. If you need to access a type in this assembly from 23 | // COM, set the ComVisible attribute to true on that type. 24 | 25 | [assembly: ComVisible(false)] 26 | 27 | // The following GUID is for the ID of the typelib if this project is exposed to COM 28 | 29 | [assembly: Guid("05d2da44-1b8e-4cf7-94ed-4d52451cd095")] 30 | 31 | // Version information for an assembly consists of the following four values: 32 | // 33 | // Major Version 34 | // Minor Version 35 | // Build Number 36 | // Revision 37 | // 38 | // You can specify all the values or you can default the Build and Revision Numbers 39 | // by using the '*' as shown below: 40 | // [assembly: AssemblyVersion("1.0.*")] 41 | 42 | [assembly: AssemblyVersion("1.0.0.0")] 43 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

PokemonGo.RocketAPI

3 | 4 | 5 | API stability 6 | 7 |
8 | 9 | Interface to Pokémon Go Client including pretty much every call 10 | 11 | **Read previous issues before opening a new one! Maybe your issue is already answered. Questions will be removed. 12 | 13 | ---------- 14 | ### Usage Example 15 | 16 | ``` 17 | var client = new Client(new Settings()); //Define your own ISettings implementation 18 | await _client.Login.DoGoogleLogin(); 19 | var inventory = await _client.Inventory.GetInventory().ConfigureAwait(false); 20 | var profile = await _client.Player.GetOwnProfile().ConfigureAwait(false); 21 | var playerStats = await _inventory.GetPlayerStats().ConfigureAwait(false); 22 | var settings = await _client.Download.GetSettings().ConfigureAwait(false); 23 | var mapObjects = await _client.Map.GetMapObjects().ConfigureAwait(false); 24 | var updateLocation = await _client.Player.UpdatePlayerLocation().ConfigureAwait(false); 25 | var encounter = await _client.Encounter.EncounterPokemon(encId, spawnId).ConfigureAwait(false); 26 | var catchPokemon = await _client.Encounter.CatchPokemon(pokemon.EncounterId, pokemon.SpawnPointId, pokeball).ConfigureAwait(false) 27 | var evolvePokemon = await _client.Inventory.EvolvePokemon(pokemonId).ConfigureAwait(false); 28 | var transfer = await _client.Inventory.TransferPokemon(pokemonId).ConfigureAwait(false); 29 | var recycle = await _client.Inventory.RecycleItem(item.ItemId, item.Count).ConfigureAwait(false); 30 | var useBerry = await _client.Encounter.UseCaptureItem(encounterId, ItemId.ItemRazzBerry, spawnPointId).ConfigureAwait(false); 31 | var fortInfo = await _client.Fort.GetFort(pokeStopId, pokeStopLatitude, pokeStopLongitude).ConfigureAwait(false); 32 | var fortSearch = await _client.Fort.SearchFort(pokeStopId, pokeStopLatitude, pokeStopLongitude).ConfigureAwait(false); 33 | 34 | and a lot more :) 35 | 36 | You can visit Pokestops, encounter Pokemon (normal/lure/incense), catch Pokemon, drop items, use items and everything else :) 37 | ``` 38 | 39 | ---------- 40 | 41 | ### What is Pokémon Go? 42 | According to [the company](http://www.pokemon.com/us/pokemon-video-games/pokemon-go/): 43 | 44 | > “Travel between the real world and the virtual world of Pokémon with Pokémon GO for iPhone and Android devices. With Pokémon GO, you’ll discover Pokémon in a whole new world—your own! Pokémon GO is built on Niantic’s Real World Gaming Platform and will use real locations to encourage players to search far and wide in the real world to discover Pokémon. Pokémon GO allows you to find and catch more than a hundred species of Pokémon as you explore your surroundings.” 45 | 46 | # License 47 | 48 | This Project is licensed as GNU (GNU GENERAL PUBLIC LICENSE v3) 49 | 50 | ## Legal 51 | 52 | This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by Niantic, The Pokémon Company, Nintendo or any of its affiliates or subsidiaries. This is an independent and unofficial API for educational use ONLY. Use at your own risk. 53 | 54 | ## Credits 55 | 56 | Credits to AeonLucid, johnduhart and for making public proto available. Saved a lot of work! 57 | -------------------------------------------------------------------------------- /Rpc/BaseRpc.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Threading.Tasks; 5 | using Google.Protobuf; 6 | using PokemonGo.RocketAPI.Extensions; 7 | using PokemonGo.RocketAPI.Helpers; 8 | using POGOProtos.Networking.Envelopes; 9 | using POGOProtos.Networking.Requests; 10 | 11 | #endregion 12 | 13 | namespace PokemonGo.RocketAPI.Rpc 14 | { 15 | public class BaseRpc 16 | { 17 | protected Client Client; 18 | 19 | protected BaseRpc(Client client) 20 | { 21 | Client = client; 22 | } 23 | 24 | protected RequestBuilder GetRequestBuilder() 25 | { 26 | return Client.RequestBuilder; 27 | } 28 | 29 | protected async Task PostProtoPayload(RequestType type, 30 | IMessage message) where TRequest : IMessage 31 | where TResponsePayload : IMessage, new() 32 | { 33 | var requestEnvelops = await GetRequestBuilder().GetRequestEnvelope(type, message).ConfigureAwait(false); 34 | return await Client.PokemonHttpClient.PostProtoPayload(Client, requestEnvelops).ConfigureAwait(false); 35 | } 36 | 37 | protected async Task PostProtoPayload( 38 | RequestEnvelope requestEnvelope) where TRequest : IMessage 39 | where TResponsePayload : IMessage, new() 40 | { 41 | return await Client.PokemonHttpClient.PostProtoPayload(Client, requestEnvelope).ConfigureAwait(false); 42 | } 43 | 44 | protected async Task> PostProtoPayload(RequestEnvelope requestEnvelope) 45 | where TRequest : IMessage 46 | where T1 : class, IMessage, new() 47 | where T2 : class, IMessage, new() 48 | { 49 | var responses = await PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2)).ConfigureAwait(false); 50 | return new Tuple(responses[0] as T1, responses[1] as T2); 51 | } 52 | 53 | protected async Task> PostProtoPayload(RequestEnvelope requestEnvelope) 54 | where TRequest : IMessage 55 | where T1 : class, IMessage, new() 56 | where T2 : class, IMessage, new() 57 | where T3 : class, IMessage, new() 58 | { 59 | var responses = await PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2), typeof(T3)).ConfigureAwait(false); 60 | return new Tuple(responses[0] as T1, responses[1] as T2, responses[2] as T3); 61 | } 62 | 63 | protected async Task> PostProtoPayload( 64 | RequestEnvelope requestEnvelope) where TRequest : IMessage 65 | where T1 : class, IMessage, new() 66 | where T2 : class, IMessage, new() 67 | where T3 : class, IMessage, new() 68 | where T4 : class, IMessage, new() 69 | { 70 | var responses = 71 | await PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2), typeof(T3), typeof(T4)).ConfigureAwait(false); 72 | return new Tuple(responses[0] as T1, responses[1] as T2, responses[2] as T3, 73 | responses[3] as T4); 74 | } 75 | 76 | protected async Task> PostProtoPayload( 77 | RequestEnvelope requestEnvelope) where TRequest : IMessage 78 | where T1 : class, IMessage, new() 79 | where T2 : class, IMessage, new() 80 | where T3 : class, IMessage, new() 81 | where T4 : class, IMessage, new() 82 | where T5 : class, IMessage, new() 83 | { 84 | var responses = 85 | await 86 | PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2), typeof(T3), typeof(T4), 87 | typeof(T5)).ConfigureAwait(false); 88 | return new Tuple(responses[0] as T1, responses[1] as T2, responses[2] as T3, 89 | responses[3] as T4, responses[4] as T5); 90 | } 91 | 92 | protected async Task> PostProtoPayload( 93 | RequestEnvelope requestEnvelope) where TRequest : IMessage 94 | where T1 : class, IMessage, new() 95 | where T2 : class, IMessage, new() 96 | where T3 : class, IMessage, new() 97 | where T4 : class, IMessage, new() 98 | where T5 : class, IMessage, new() 99 | where T6 : class, IMessage, new() 100 | { 101 | var responses = 102 | await 103 | PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2), typeof(T3), typeof(T4), 104 | typeof(T5), typeof(T6)).ConfigureAwait(false); 105 | return new Tuple(responses[0] as T1, responses[1] as T2, responses[2] as T3, 106 | responses[3] as T4, responses[4] as T5, responses[5] as T6); 107 | } 108 | 109 | protected async Task> PostProtoPayload( 110 | RequestEnvelope requestEnvelope) where TRequest : IMessage 111 | where T1 : class, IMessage, new() 112 | where T2 : class, IMessage, new() 113 | where T3 : class, IMessage, new() 114 | where T4 : class, IMessage, new() 115 | where T5 : class, IMessage, new() 116 | where T6 : class, IMessage, new() 117 | where T7 : class, IMessage, new() 118 | { 119 | var responses = 120 | await 121 | PostProtoPayload(requestEnvelope, typeof(T1), typeof(T2), typeof(T3), typeof(T4), 122 | typeof(T5), typeof(T6), typeof(T7)).ConfigureAwait(false); 123 | return new Tuple(responses[0] as T1, responses[1] as T2, responses[2] as T3, 124 | responses[3] as T4, responses[4] as T5, responses[5] as T6, responses[6] as T7); 125 | } 126 | 127 | protected async Task PostProtoPayload(RequestEnvelope requestEnvelope, 128 | params Type[] responseTypes) where TRequest : IMessage 129 | { 130 | return 131 | await 132 | Client.PokemonHttpClient.PostProtoPayload(Client, requestEnvelope, responseTypes).ConfigureAwait(false); 133 | } 134 | 135 | protected async Task PostProto(RequestEnvelope requestEnvelope) 136 | where TRequest : IMessage 137 | { 138 | return await Client.PokemonHttpClient.PerformThrottledRemoteProcedureCall(Client, requestEnvelope).ConfigureAwait(false); 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /Rpc/Download.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Collections.Generic; 4 | using System.Threading.Tasks; 5 | using POGOProtos.Networking.Requests; 6 | using POGOProtos.Networking.Requests.Messages; 7 | using POGOProtos.Networking.Responses; 8 | using System; 9 | using PokemonGo.RocketAPI.Helpers; 10 | using Google.Protobuf; 11 | using Google.Protobuf.Collections; 12 | 13 | #endregion 14 | 15 | namespace PokemonGo.RocketAPI.Rpc 16 | { 17 | public class Download : BaseRpc 18 | { 19 | public Download(Client client) : base(client) 20 | { 21 | } 22 | 23 | public RepeatedField ItemTemplates { get; set; } 24 | 25 | public async Task GetItemTemplates() 26 | { 27 | var downloadItemTemplatesRequest = new Request 28 | { 29 | RequestType = RequestType.DownloadItemTemplates, 30 | RequestMessage = ((IMessage)new DownloadItemTemplatesMessage() 31 | { 32 | // To be implemented 33 | //PageTimestamp = 0, 34 | //Paginate = true, 35 | //PageOffset = Client.PageOffset 36 | }).ToByteString() 37 | }; 38 | 39 | var requestEnvelope = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(downloadItemTemplatesRequest, Client)).ConfigureAwait(false); 40 | 41 | Tuple response = 42 | await 43 | PostProtoPayload 44 | (requestEnvelope).ConfigureAwait(false); 46 | 47 | DownloadItemTemplatesResponse downloadItemTemplatesResponse = response.Item1; 48 | ItemTemplates = downloadItemTemplatesResponse.ItemTemplates; 49 | PokemonMeta.Update(downloadItemTemplatesResponse); 50 | 51 | CheckChallengeResponse checkChallengeResponse = response.Item2; 52 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 53 | 54 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 55 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 56 | 57 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 58 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 59 | 60 | return response.Item1; 61 | } 62 | 63 | public async Task GetRemoteConfigVersion() 64 | { 65 | var requestEnvelope = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(CommonRequest.GetDownloadRemoteConfigVersionMessageRequest(Client), Client, RequestType.GetBuddyWalked)).ConfigureAwait(false); 66 | 67 | Tuple response = 68 | await 69 | PostProtoPayload 70 | (requestEnvelope).ConfigureAwait(false); 72 | 73 | CheckChallengeResponse checkChallengeResponse = response.Item2; 74 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 75 | 76 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 77 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 78 | 79 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 80 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 81 | 82 | return response.Item1; 83 | } 84 | 85 | public async Task GetAssetDigest() 86 | { 87 | var requestEnvelope = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(CommonRequest.GetGetAssetDigestMessageRequest(Client), Client, RequestType.GetBuddyWalked)).ConfigureAwait(false); 88 | 89 | Tuple response = 90 | await 91 | PostProtoPayload 92 | (requestEnvelope).ConfigureAwait(false); 94 | 95 | CheckChallengeResponse checkChallengeResponse = response.Item2; 96 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 97 | 98 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 99 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 100 | 101 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 102 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 103 | 104 | return response.Item1; 105 | } 106 | 107 | public async Task GetDownloadUrls(IEnumerable assetIds) 108 | { 109 | var getDownloadUrlsRequest = new Request 110 | { 111 | RequestType = RequestType.GetDownloadUrls, 112 | RequestMessage = new GetDownloadUrlsMessage 113 | { 114 | AssetId = { assetIds } 115 | }.ToByteString() 116 | }; 117 | 118 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(getDownloadUrlsRequest, Client)).ConfigureAwait(false); 119 | 120 | Tuple response = 121 | await 122 | PostProtoPayload 123 | (request).ConfigureAwait(false); 125 | 126 | CheckChallengeResponse checkChallengeResponse = response.Item2; 127 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 128 | 129 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 130 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 131 | 132 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 133 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 134 | 135 | return response.Item1; 136 | } 137 | 138 | public async Task DownloadGmTemplates(long basisBatchId, long batchId, int pageOffset) 139 | { 140 | var DownloadGmTemplatesRequest = new Request 141 | { 142 | RequestType = RequestType.DownloadGameMasterTemplates, 143 | RequestMessage = ((IMessage)new DownloadGmTemplatesMessage 144 | { 145 | BasisBatchId = basisBatchId, 146 | BatchId = batchId, 147 | PageOffset = pageOffset 148 | }).ToByteString() 149 | }; 150 | 151 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(DownloadGmTemplatesRequest, Client)).ConfigureAwait(false); 152 | 153 | Tuple response = 154 | await 155 | PostProtoPayload 156 | (request).ConfigureAwait(false); 158 | 159 | /* 160 | * maybe not needed 161 | * 162 | CheckChallengeResponse checkChallengeResponse = response.Item2; 163 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 164 | 165 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 166 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 167 | 168 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 169 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 170 | */ 171 | 172 | return response.Item1; 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /Rpc/Encounter.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System.Threading.Tasks; 4 | using POGOProtos.Enums; 5 | using POGOProtos.Inventory.Item; 6 | using POGOProtos.Networking.Requests; 7 | using POGOProtos.Networking.Requests.Messages; 8 | using POGOProtos.Networking.Responses; 9 | using Google.Protobuf; 10 | using PokemonGo.RocketAPI.Helpers; 11 | using System; 12 | using POGOProtos.Map.Fort; 13 | using POGOProtos.Data; 14 | 15 | #endregion 16 | 17 | namespace PokemonGo.RocketAPI.Rpc 18 | { 19 | public class Encounter : BaseRpc 20 | { 21 | public Encounter(Client client) : base(client) 22 | { 23 | } 24 | 25 | public async Task EncounterPokemon(ulong encounterId, string spawnPointGuid) 26 | { 27 | var encounterPokemonRequest = new Request 28 | { 29 | RequestType = RequestType.Encounter, 30 | RequestMessage = ((IMessage)new EncounterMessage 31 | { 32 | EncounterId = encounterId, 33 | SpawnPointId = spawnPointGuid, 34 | PlayerLatitude = Client.CurrentLatitude, 35 | PlayerLongitude = Client.CurrentLongitude 36 | }).ToByteString() 37 | }; 38 | 39 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(encounterPokemonRequest, Client)).ConfigureAwait(false); 40 | 41 | Tuple response = 42 | await 43 | PostProtoPayload 44 | (request).ConfigureAwait(false); 46 | 47 | CheckChallengeResponse checkChallengeResponse = response.Item2; 48 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 49 | 50 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 51 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 52 | 53 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 54 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 55 | 56 | return response.Item1; 57 | } 58 | 59 | public async Task UseCaptureItem(ulong encounterId, ItemId itemId, string spawnPointId) 60 | { 61 | var useCaptureItemRequest = new Request 62 | { 63 | RequestType = RequestType.UseItemCapture, 64 | RequestMessage = ((IMessage)new UseItemCaptureMessage 65 | { 66 | EncounterId = encounterId, 67 | ItemId = itemId, 68 | SpawnPointId = spawnPointId 69 | }).ToByteString() 70 | }; 71 | 72 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(useCaptureItemRequest, Client)).ConfigureAwait(false); 73 | 74 | Tuple response = 75 | await 76 | PostProtoPayload 77 | (request).ConfigureAwait(false); 79 | 80 | CheckChallengeResponse checkChallengeResponse = response.Item2; 81 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 82 | 83 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 84 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 85 | 86 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 87 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 88 | 89 | return response.Item1; 90 | } 91 | 92 | 93 | public async Task UseItemEncounter(ulong encounterId, ItemId itemId, string spawnPointId) 94 | { 95 | var useCaptureItemRequest = new Request 96 | { 97 | RequestType = RequestType.UseItemEncounter, 98 | RequestMessage = ((IMessage)new UseItemEncounterMessage 99 | { 100 | EncounterId = encounterId, 101 | Item = itemId, 102 | SpawnPointGuid = spawnPointId 103 | }).ToByteString() 104 | }; 105 | 106 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(useCaptureItemRequest, Client)).ConfigureAwait(false); 107 | 108 | Tuple response = 109 | await 110 | PostProtoPayload 111 | (request).ConfigureAwait(false); 113 | 114 | CheckChallengeResponse checkChallengeResponse = response.Item2; 115 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 116 | 117 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 118 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 119 | 120 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 121 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 122 | 123 | return response.Item1; 124 | } 125 | 126 | public async Task CatchPokemon(ulong encounterId, string spawnPointGuid, 127 | ItemId pokeballItemId, double normalizedRecticleSize = 1.950, double spinModifier = 1, 128 | bool hitPokemon = true, double normalizedHitPos = 1) 129 | { 130 | var arPlusValues = new ARPlusEncounterValues(); 131 | APIConfiguration.Logger.LogInfo("Using AR Bonus Values"); 132 | arPlusValues.Awareness = (float)0.000; 133 | arPlusValues.Proximity = (float)0.000; 134 | arPlusValues.PokemonFrightened = false; 135 | 136 | var catchPokemonRequest = new Request 137 | { 138 | RequestType = RequestType.CatchPokemon, 139 | RequestMessage = ((IMessage)new CatchPokemonMessage 140 | { 141 | ArPlusValues = arPlusValues, 142 | EncounterId = encounterId, 143 | Pokeball = pokeballItemId, 144 | SpawnPointId = spawnPointGuid, 145 | HitPokemon = hitPokemon, 146 | NormalizedReticleSize = normalizedRecticleSize, 147 | SpinModifier = spinModifier, 148 | NormalizedHitPosition = normalizedHitPos 149 | }).ToByteString() 150 | }; 151 | 152 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(catchPokemonRequest, Client)).ConfigureAwait(false); 153 | 154 | Tuple response = 155 | await 156 | PostProtoPayload 157 | (request).ConfigureAwait(false); 159 | 160 | CheckChallengeResponse checkChallengeResponse = response.Item2; 161 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 162 | 163 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 164 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 165 | 166 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 167 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 168 | 169 | return response.Item1; 170 | } 171 | 172 | public async Task EncounterIncensePokemon(ulong encounterId, string encounterLocation) 173 | { 174 | var encounterIncensePokemonRequest = new Request 175 | { 176 | RequestType = RequestType.IncenseEncounter, 177 | RequestMessage = ((IMessage)new IncenseEncounterMessage 178 | { 179 | EncounterId = encounterId, 180 | EncounterLocation = encounterLocation 181 | }).ToByteString() 182 | }; 183 | 184 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(encounterIncensePokemonRequest, Client)).ConfigureAwait(false); 185 | 186 | Tuple response = 187 | await 188 | PostProtoPayload 189 | (request).ConfigureAwait(false); 191 | 192 | CheckChallengeResponse checkChallengeResponse = response.Item2; 193 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 194 | 195 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 196 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 197 | 198 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 199 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 200 | 201 | return response.Item1; 202 | } 203 | 204 | public async Task EncounterLurePokemon(ulong encounterId, FortData fort) 205 | { 206 | var encounterLurePokemonRequest = new Request 207 | { 208 | RequestType = RequestType.DiskEncounter, 209 | RequestMessage = ((IMessage)new DiskEncounterMessage 210 | { 211 | EncounterId = encounterId, 212 | FortId = fort.Id, 213 | PlayerLatitude = Client.CurrentLatitude, 214 | PlayerLongitude = Client.CurrentLongitude, 215 | GymLatDegrees = fort.Latitude, 216 | GymLngDegrees = fort.Longitude 217 | }).ToByteString() 218 | }; 219 | 220 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(encounterLurePokemonRequest, Client)).ConfigureAwait(false); 221 | 222 | Tuple response = 223 | await 224 | PostProtoPayload 225 | (request).ConfigureAwait(false); 227 | 228 | CheckChallengeResponse checkChallengeResponse = response.Item2; 229 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 230 | 231 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 232 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 233 | 234 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 235 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 236 | 237 | return response.Item1; 238 | } 239 | 240 | public async Task EncounterTutorialComplete(PokemonId pokemonId) 241 | { 242 | var encounterTutorialCompleteRequest = new Request 243 | { 244 | RequestType = RequestType.EncounterTutorialComplete, 245 | RequestMessage = ((IMessage)new EncounterTutorialCompleteMessage 246 | { 247 | PokemonId = pokemonId 248 | }).ToByteString() 249 | }; 250 | 251 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(encounterTutorialCompleteRequest, Client)).ConfigureAwait(false); 252 | 253 | Tuple response = 254 | await 255 | PostProtoPayload 256 | (request).ConfigureAwait(false); 258 | 259 | CheckChallengeResponse checkChallengeResponse = response.Item2; 260 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 261 | 262 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 263 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 264 | 265 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 266 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 267 | 268 | return response.Item1; 269 | } 270 | } 271 | } -------------------------------------------------------------------------------- /Rpc/Login.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Threading.Tasks; 5 | using PokemonGo.RocketAPI.Enums; 6 | using PokemonGo.RocketAPI.Exceptions; 7 | using PokemonGo.RocketAPI.Helpers; 8 | using POGOProtos.Networking.Responses; 9 | using System.IO; 10 | using Newtonsoft.Json; 11 | using System.Threading; 12 | using PokemonGo.RocketAPI.LoginProviders; 13 | using PokemonGo.RocketAPI.Authentication.Data; 14 | using POGOProtos.Enums; 15 | using System.Collections.Generic; 16 | using Google.Protobuf.Collections; 17 | 18 | #endregion 19 | 20 | namespace PokemonGo.RocketAPI.Rpc 21 | { 22 | public delegate void GoogleDeviceCodeDelegate(string code, string uri); 23 | 24 | public class Login : BaseRpc 25 | { 26 | private Semaphore ReauthenticateMutex { get; } = new Semaphore(1, 1); 27 | public Login(Client client) : base(client) 28 | { 29 | Client.LoginProvider = SetLoginType(client.Settings); 30 | Client.ApiUrl = Constants.RpcUrl; 31 | } 32 | 33 | private ILoginProvider SetLoginType(ISettings settings) 34 | { 35 | switch (settings.AuthType) 36 | { 37 | case AuthType.Google: 38 | return new GoogleLoginProvider(settings.Username, settings.Password); 39 | case AuthType.Ptc: 40 | return new PtcLoginProvider(settings.Username, settings.Password); 41 | default: 42 | throw new ArgumentOutOfRangeException(nameof(settings.AuthType), "Unknown AuthType"); 43 | } 44 | } 45 | 46 | private bool IsValidAccessToken() 47 | { 48 | if (Client.AccessToken == null || string.IsNullOrEmpty(Client.AccessToken.Token) || Client.AccessToken.IsExpired) 49 | return false; 50 | 51 | return true; 52 | } 53 | 54 | public async Task GetValidAccessToken(bool forceRefresh = false, bool isCached = false) 55 | { 56 | try 57 | { 58 | ReauthenticateMutex.WaitOne(); 59 | 60 | if (forceRefresh) 61 | { 62 | Client.AccessToken.Expire(); 63 | if (isCached) 64 | DeleteSavedAccessToken(); 65 | } 66 | 67 | if (IsValidAccessToken()) 68 | return Client.AccessToken; 69 | 70 | // If we got here then access token is expired or not loaded into memory. 71 | if (isCached) 72 | { 73 | var loginProvider = Client.LoginProvider; 74 | var cacheDir = Path.Combine(Directory.GetCurrentDirectory(), "Cache"); 75 | var fileName = Path.Combine(cacheDir, $"{loginProvider.UserId}-{loginProvider.ProviderId}.json"); 76 | 77 | if (!Directory.Exists(cacheDir)) 78 | Directory.CreateDirectory(cacheDir); 79 | 80 | if (File.Exists(fileName)) 81 | { 82 | var accessToken = JsonConvert.DeserializeObject(File.ReadAllText(fileName)); 83 | 84 | if (!accessToken.IsExpired) 85 | { 86 | Client.AccessToken = accessToken; 87 | return accessToken; 88 | } 89 | } 90 | } 91 | 92 | await Reauthenticate(isCached).ConfigureAwait(false); 93 | return Client.AccessToken; 94 | } 95 | finally 96 | { 97 | ReauthenticateMutex.Release(); 98 | } 99 | } 100 | 101 | private void SaveAccessToken() 102 | { 103 | if (!IsValidAccessToken()) 104 | return; 105 | 106 | var fileName = Path.Combine(Directory.GetCurrentDirectory(), "Cache", $"{Client.AccessToken.Uid}.json"); 107 | 108 | File.WriteAllText(fileName, JsonConvert.SerializeObject(Client.AccessToken, Formatting.Indented)); 109 | } 110 | 111 | private void DeleteSavedAccessToken() 112 | { 113 | var cacheDir = Path.Combine(Directory.GetCurrentDirectory(), "Cache"); 114 | var fileName = Path.Combine(cacheDir, $"{Client.AccessToken?.Uid}-{Client.LoginProvider.ProviderId}.json"); 115 | if (File.Exists(fileName)) 116 | File.Delete(fileName); 117 | } 118 | 119 | private async Task Reauthenticate(bool isCached) 120 | { 121 | var tries = 0; 122 | while (!IsValidAccessToken()) 123 | { 124 | // If expired, then we always delete the saved access token if it exists. 125 | if (isCached) 126 | DeleteSavedAccessToken(); 127 | 128 | try 129 | { 130 | Client.AccessToken = await Client.LoginProvider.GetAccessToken().ConfigureAwait(false); 131 | } 132 | catch (Exception ex) 133 | { 134 | APIConfiguration.Logger.LogError(ex.Message); 135 | 136 | if (ex.Message.Contains("15 minutes")) throw new PtcLoginException(ex.Message); 137 | 138 | if (ex.Message.Contains("You have to log into a browser")) throw new GoogleTwoFactorException(ex.Message); 139 | //Logger.Error($"Reauthenticate exception was catched: {exception}"); 140 | } 141 | finally 142 | { 143 | if (!IsValidAccessToken()) 144 | { 145 | var sleepSeconds = Math.Min(60, ++tries * 5); 146 | //Logger.Error($"Reauthentication failed, trying again in {sleepSeconds} seconds."); 147 | await Task.Delay(TimeSpan.FromMilliseconds(sleepSeconds * 1000)).ConfigureAwait(false); 148 | } 149 | else 150 | { 151 | // We have successfully refreshed the token so save it. 152 | if (isCached) 153 | SaveAccessToken(); 154 | } 155 | 156 | if (tries == 5) 157 | { 158 | throw new TokenRefreshException("Error refreshing access token."); 159 | } 160 | } 161 | } 162 | } 163 | 164 | public async Task DoLogin() 165 | { 166 | Client.Reset(); 167 | 168 | // Don't wait for background start of killswitch. 169 | // jjskuld - Ignore CS4014 warning for now. 170 | #pragma warning disable 4014 171 | Client.KillswitchTask.Start(); 172 | #pragma warning restore 4014 173 | 174 | GetPlayerResponse player = await Client.Player.GetPlayer(false, true).ConfigureAwait(false); // Set false because initial GetPlayer does not use common requests. 175 | if (player.Warn) 176 | { 177 | APIConfiguration.Logger.LogFlaggedInit($"This account {Client.Player.PlayerData.Username} seems to be flagged, it is recommended to not use bot on this account for now!"); 178 | if (Client.Settings.AutoExitBotIfAccountFlagged) 179 | { 180 | APIConfiguration.Logger.LogFlaggedInit("\n\rThe bot will close in 10 seconds."); 181 | Thread.Sleep(10000); 182 | Environment.Exit(0); 183 | } 184 | } 185 | 186 | if (player.Banned) 187 | { 188 | APIConfiguration.Logger.LogErrorInit("This account seems to be banned"); 189 | } 190 | 191 | APIConfiguration.Logger.LogDebug("GetPlayer done."); 192 | await RandomHelper.RandomDelay(10000).ConfigureAwait(false); 193 | 194 | await Client.Download.GetRemoteConfigVersion().ConfigureAwait(false); 195 | APIConfiguration.Logger.LogDebug("GetRemoteConfigVersion done."); 196 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 197 | 198 | //var getAssetDigest = await Client.Download.GetAssetDigest().ConfigureAwait(false); 199 | //Client.PageOffset = getAssetDigest.PageOffset; 200 | await Client.Download.GetAssetDigest().ConfigureAwait(false); 201 | APIConfiguration.Logger.LogDebug("GetAssetDigest done."); 202 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 203 | 204 | //var getItemTemplates = await Client.Download.GetItemTemplates().ConfigureAwait(false); 205 | //Client.PageOffset = getItemTemplates.PageOffset; 206 | await Client.Download.GetItemTemplates().ConfigureAwait(false); 207 | APIConfiguration.Logger.LogDebug("GetItemTemplates done."); 208 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 209 | 210 | //Need get storeitems 211 | 212 | //New on protos: 213 | var ids = await Client.Misc.FetchAllNews().ConfigureAwait(false); 214 | var newids = new RepeatedField(); 215 | foreach (var id in ids.CurrentNews.NewsArticles) 216 | newids.Add(id.Id); 217 | APIConfiguration.Logger.LogDebug("FetchAllNews done."); 218 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 219 | await Client.Misc.MarkReadNewsArticle(newids).ConfigureAwait(false); 220 | APIConfiguration.Logger.LogDebug("MarkReadNewsArticle done."); 221 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 222 | 223 | await Client.Player.GetPlayerProfile().ConfigureAwait(false); 224 | APIConfiguration.Logger.LogDebug("GetPlayerProfile done."); 225 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 226 | 227 | GetInboxResponse req = await Client.Misc.GetInbox(true, false, 0L).ConfigureAwait(false); 228 | CommonRequest.ProcessGetInboxResponse(Client, req); 229 | await RandomHelper.RandomDelay(300).ConfigureAwait(false); 230 | 231 | return player; 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /Rpc/Map.cs: -------------------------------------------------------------------------------- 1 | #region using directives 2 | 3 | using System; 4 | using System.Threading.Tasks; 5 | using Google.Protobuf; 6 | using PokemonGo.RocketAPI.Helpers; 7 | using POGOProtos.Networking.Requests; 8 | using POGOProtos.Networking.Requests.Messages; 9 | using POGOProtos.Networking.Responses; 10 | using System.Linq; 11 | using GeoCoordinatePortable; 12 | 13 | #endregion 14 | 15 | namespace PokemonGo.RocketAPI.Rpc 16 | { 17 | public class Map : BaseRpc 18 | { 19 | public GetMapObjectsResponse LastGetMapObjectResponse; 20 | internal long LastRpcMapObjectsRequestMs { get; private set; } 21 | internal GeoCoordinate LastGeoCoordinateMapObjectsRequest { get; private set; } 22 | 23 | public Map(Client client) : base(client) 24 | { 25 | } 26 | 27 | private int GetMilisSecondUntilRefreshMapAvail() 28 | { 29 | var minSeconds = Client.GlobalSettings.MapSettings.GetMapObjectsMinRefreshSeconds; 30 | var lastGeoCoordinate = LastGeoCoordinateMapObjectsRequest; 31 | var secondsSinceLast = Util.TimeUtil.GetCurrentTimestampInMilliseconds() - LastRpcMapObjectsRequestMs; 32 | 33 | //if (lastGeoCoordinate == null) 34 | //{ 35 | // return 0; 36 | //} 37 | if (secondsSinceLast > minSeconds * 1000) return 0; 38 | 39 | int waitTime = (int)Math.Max(minSeconds*1000- secondsSinceLast, 0); 40 | 41 | return waitTime; 42 | } 43 | private bool CanRefreshMap() 44 | { 45 | var minSeconds = Client.GlobalSettings.MapSettings.GetMapObjectsMinRefreshSeconds; 46 | var maxSeconds = Client.GlobalSettings.MapSettings.GetMapObjectsMaxRefreshSeconds; 47 | var minDistance = Client.GlobalSettings.MapSettings.GetMapObjectsMinDistanceMeters; 48 | var lastGeoCoordinate = LastGeoCoordinateMapObjectsRequest; 49 | var secondsSinceLast = (Util.TimeUtil.GetCurrentTimestampInMilliseconds() - LastRpcMapObjectsRequestMs) * 1000; 50 | 51 | if (lastGeoCoordinate == null) 52 | { 53 | return true; 54 | } 55 | else if (secondsSinceLast >= minSeconds) 56 | { 57 | var metersMoved = new GeoCoordinate(Client.CurrentLatitude, Client.CurrentLongitude).GetDistanceTo(lastGeoCoordinate); 58 | if (secondsSinceLast >= maxSeconds) 59 | { 60 | return true; 61 | } 62 | else if (metersMoved >= minDistance) 63 | { 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | /// 71 | /// 72 | /// 73 | /// For thread wait until next api call available to use 74 | /// Allow update cache, in some case we don't want update cache, snipe pokemon is an example 75 | /// 76 | public async Task GetMapObjects(bool force = false, bool updateCache=true) 77 | { 78 | if (force) 79 | { 80 | var t = GetMilisSecondUntilRefreshMapAvail(); 81 | //wait until get map available 82 | if (t > 0) 83 | { 84 | await Task.Delay(t).ConfigureAwait(false); 85 | } 86 | } 87 | if (!CanRefreshMap()) 88 | { 89 | return force ? await GetMapObjects(force, updateCache).ConfigureAwait(false) : LastGetMapObjectResponse; 90 | // If we cannot refresh the map, return the cached response. 91 | } 92 | 93 | var lat = Client.CurrentLatitude; 94 | var lon = Client.CurrentLongitude; 95 | 96 | var getMapObjectsMessage = new GetMapObjectsMessage 97 | { 98 | CellId = { S2Helper.GetNearbyCellIds(lon, lat) }, 99 | SinceTimestampMs = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 100 | Latitude = lat, 101 | Longitude = lon 102 | }; 103 | 104 | var getMapObjectsRequest = new Request 105 | { 106 | RequestType = RequestType.GetMapObjects, 107 | RequestMessage = getMapObjectsMessage.ToByteString() 108 | }; 109 | 110 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(getMapObjectsRequest, Client)).ConfigureAwait(false); 111 | 112 | Tuple response = 113 | await 114 | PostProtoPayload 115 | (request).ConfigureAwait(false); 117 | 118 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 119 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 120 | 121 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 122 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 123 | 124 | CheckChallengeResponse checkChallengeResponse = response.Item2; 125 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 126 | 127 | LastRpcMapObjectsRequestMs = Util.TimeUtil.GetCurrentTimestampInMilliseconds(); 128 | 129 | var numCells = response.Item1.MapCells.Count; 130 | var numCellsWithForts = response.Item1.MapCells.Count(x => x.Forts.Count > 0); 131 | var numCellsWithNearbyPokemon = response.Item1.MapCells.Count(x => x.NearbyPokemons.Count > 0); 132 | var numCellsWithWildPokemon = response.Item1.MapCells.Count(x => x.WildPokemons.Count > 0); 133 | 134 | // Only cache good responses 135 | if (updateCache && 136 | numCells > 0 && 137 | (numCellsWithForts > 0 || 138 | numCellsWithNearbyPokemon > 0 || 139 | numCellsWithWildPokemon > 0)) 140 | { 141 | // Good map response since we got at least a fort or pokemon in our cells. 142 | LastGetMapObjectResponse = response.Item1; 143 | LastGeoCoordinateMapObjectsRequest = new GeoCoordinate(lat, lon); 144 | } 145 | 146 | if (updateCache && LastGetMapObjectResponse == null) 147 | { 148 | LastGetMapObjectResponse = response.Item1; 149 | 150 | } 151 | 152 | return response.Item1; 153 | } 154 | 155 | public async Task GetIncensePokemons() 156 | { 157 | var getIncensePokemonsRequest = new Request 158 | { 159 | RequestType = RequestType.GetIncensePokemon, 160 | RequestMessage = ((IMessage)new GetIncensePokemonMessage 161 | { 162 | PlayerLatitude = Client.CurrentLatitude, 163 | PlayerLongitude = Client.CurrentLongitude 164 | }).ToByteString() 165 | }; 166 | 167 | var request = await GetRequestBuilder().GetRequestEnvelope(CommonRequest.FillRequest(getIncensePokemonsRequest, Client)).ConfigureAwait(false); 168 | 169 | Tuple response = 170 | await 171 | PostProtoPayload 172 | (request).ConfigureAwait(false); 174 | 175 | CheckChallengeResponse checkChallengeResponse = response.Item2; 176 | CommonRequest.ProcessCheckChallengeResponse(Client, checkChallengeResponse); 177 | 178 | GetHoloInventoryResponse getHoloInventoryResponse = response.Item4; 179 | CommonRequest.ProcessGetHoloInventoryResponse(Client, getHoloInventoryResponse); 180 | 181 | DownloadSettingsResponse downloadSettingsResponse = response.Item6; 182 | CommonRequest.ProcessDownloadSettingsResponse(Client, downloadSettingsResponse); 183 | 184 | return response.Item1; 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.0.{build} 2 | branches: 3 | only: 4 | - master 5 | skip_tags: true 6 | image: Visual Studio 2017 7 | configuration: Release 8 | platform: x86 9 | build: 10 | verbosity: minimal 11 | environment: 12 | SolutionDirectory: . 13 | before_build: 14 | - cmd: >- 15 | sed -i "s#$(SolutionDir)#.#g" PokemonGo.RocketAPI.csproj 16 | 17 | nuget restore -OutputDirectory packages 18 | -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | --------------------------------------------------------------------------------