├── .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 |
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 |
--------------------------------------------------------------------------------