#region
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Google.Protobuf;
using PokemonGo.RocketAPI.Enums;
using PokemonGo.RocketAPI.Extensions;
using PokemonGo.RocketAPI.GeneratedCode;
using PokemonGo.RocketAPI.Helpers;
using PokemonGo.RocketAPI.Login;
using AllEnum;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using PokemonGo.RocketAPI.Exceptions;
using System.Text;
using System.IO;
using Newtonsoft.Json;
#endregion
namespace PokemonGo.RocketAPI
{
public class Client
{
private readonly HttpClient _httpClient;
private ISettings _settings;
private string _accessToken;
private string _apiUrl;
private double _currentLat;
private double _currentLng;
private Request.Types.UnknownAuth _unknownAuth;
public static string AccessToken { get; set; } = string.Empty;
private readonly ILoginType login;
public delegate void ConsoleWriteDelegate(ConsoleColor color, string message);
public static event ConsoleWriteDelegate OnConsoleWrite;
public Client(ISettings settings)
{
_settings = settings;
SetCoordinates(_settings.DefaultLatitude, _settings.DefaultLongitude);
//Setup HttpClient and create default headers
var handler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
AllowAutoRedirect = false
};
_httpClient = new HttpClient(new RetryHandler(handler));
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Niantic App");
//"Dalvik/2.1.0 (Linux; U; Android 5.1.1; SM-G900F Build/LMY48G)");
_httpClient.DefaultRequestHeaders.ExpectContinue = false;
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Connection", "keep-alive");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Accept", "*/*");
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type",
"application/x-www-form-urlencoded");
login = CreateLoginType(settings);
}
static ILoginType CreateLoginType(ISettings settings)
{
switch (settings.AuthType)
{
case AuthType.Google:
return new GoogleLogin(settings.Email, settings.Password);
case AuthType.Ptc:
return new PtcLogin(settings.PtcUsername, settings.PtcPassword);
}
throw new ArgumentOutOfRangeException(nameof(settings.AuthType), "unknown auth type");
}
public async Task<CatchPokemonResponse> CatchPokemon(ulong encounterId, string spawnPointGuid, double pokemonLat,
double pokemonLng, MiscEnums.Item pokeball, int? pokemonCP)
{
var customRequest = new Request.Types.CatchPokemonRequest
{
EncounterId = encounterId,
Pokeball = (int)GetBestBall(pokemonCP).Result,
SpawnPointGuid = spawnPointGuid,
HitPokemon = 1,
NormalizedReticleSize = Utils.FloatAsUlong(1.950),
SpinModifier = Utils.FloatAsUlong(1),
NormalizedHitPosition = Utils.FloatAsUlong(1)
};
var catchPokemonRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.CATCH_POKEMON,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, CatchPokemonResponse>($"https://{_apiUrl}/rpc",
catchPokemonRequest);
}
public async Task Login()
{
string errorMessage;
do
{
errorMessage = null;
try
{
_accessToken = await login.GetAccessToken().ConfigureAwait(false);
}
catch (LoginFailedException) { errorMessage = "Login failed - wrong username or password? - Restarting"; }
catch (PtcOfflineException) { errorMessage = "PTC login server is down - Restarting"; }
catch (JsonReaderException) { errorMessage = "Json Reader Exception - Server down? - Restarting"; }
catch (Exception ex) { errorMessage = ex.ToString() + "Exception - Please report - Restarting"; }
if (errorMessage != null)
ColoredConsoleWrite(ConsoleColor.White, errorMessage);
} while (errorMessage != null);
}
public async Task<EncounterResponse> EncounterPokemon(ulong encounterId, string spawnPointGuid)
{
var customRequest = new Request.Types.EncounterRequest
{
EncounterId = encounterId,
SpawnpointId = spawnPointGuid,
PlayerLatDegrees = Utils.FloatAsUlong(_currentLat),
PlayerLngDegrees = Utils.FloatAsUlong(_currentLng)
};
var encounterResponse = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.ENCOUNTER,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, EncounterResponse>($"https://{_apiUrl}/rpc", encounterResponse);
}
public async Task<EvolvePokemonOut> EvolvePokemon(ulong pokemonId)
{
var customRequest = new EvolvePokemon
{
PokemonId = pokemonId
};
var releasePokemonRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.EVOLVE_POKEMON,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, EvolvePokemonOut>($"https://{_apiUrl}/rpc",
releasePokemonRequest);
}
public async Task<EvolvePokemonOut> PowerUp(ulong pokemonId)
{
var customRequest = new EvolvePokemon
{
PokemonId = pokemonId
};
var releasePokemonRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.UPGRADE_POKEMON,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, EvolvePokemonOut>($"https://{_apiUrl}/rpc",
releasePokemonRequest);
}
private async Task<MiscEnums.Item> GetBestBall(int? pokemonCP)
{
var inventory = await GetInventory();
var ballCollection = inventory.InventoryDelta.InventoryItems.Select(i => i.InventoryItemData?.Item)
.Where(p => p != null)
.GroupBy(i => (MiscEnums.Item)i.Item_)
.Select(kvp => new { ItemId = kvp.Key, Amount = kvp.Sum(x => x.Count) })
.Where(y => y.ItemId == MiscEnums.Item.ITEM_POKE_BALL
|| y.ItemId == MiscEnums.Item.ITEM_GREAT_BALL
|| y.ItemId == MiscEnums.Item.ITEM_ULTRA_BALL
|| y.ItemId == MiscEnums.Item.ITEM_MASTER_BALL);
var pokeBallsCount = ballCollection.Where(p => p.ItemId == MiscEnums.Item.ITEM_POKE_BALL).
DefaultIfEmpty(new { ItemId = MiscEnums.Item.ITEM_POKE_BALL, Amount = 0 }).FirstOrDefault().Amount;
var greatBallsCount = ballCollection.Where(p => p.ItemId == MiscEnums.Item.ITEM_GREAT_BALL).
DefaultIfEmpty(new { ItemId = MiscEnums.Item.ITEM_GREAT_BALL, Amount = 0 }).FirstOrDefault().Amount;
var ultraBallsCount = ballCollection.Where(p => p.ItemId == MiscEnums.Item.ITEM_ULTRA_BALL).
DefaultIfEmpty(new { ItemId = MiscEnums.Item.ITEM_ULTRA_BALL, Amount = 0 }).FirstOrDefault().Amount;
var masterBallsCount = ballCollection.Where(p => p.ItemId == MiscEnums.Item.ITEM_MASTER_BALL).
DefaultIfEmpty(new { ItemId = MiscEnums.Item.ITEM_MASTER_BALL, Amount = 0 }).FirstOrDefault().Amount;
// Use better balls for high CP pokemon
if (masterBallsCount > 0 && pokemonCP >= 1000)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Master Ball is being used");
return MiscEnums.Item.ITEM_MASTER_BALL;
}
if (ultraBallsCount > 0 && pokemonCP >= 600)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Ultra Ball is being used");
return MiscEnums.Item.ITEM_ULTRA_BALL;
}
if (greatBallsCount > 0 && pokemonCP >= 350)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Great Ball is being used");
return MiscEnums.Item.ITEM_GREAT_BALL;
}
// If low CP pokemon, but no more pokeballs; only use better balls if pokemon are of semi-worthy quality
if (pokeBallsCount > 0)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Poke Ball is being used");
return MiscEnums.Item.ITEM_POKE_BALL;
}
else if ((greatBallsCount < 40 && pokemonCP >= 200) || greatBallsCount >= 40)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Great Ball is being used");
return MiscEnums.Item.ITEM_GREAT_BALL;
}
else if (ultraBallsCount > 0 && pokemonCP >= 500)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Ultra Ball is being used");
return MiscEnums.Item.ITEM_ULTRA_BALL;
}
else if (masterBallsCount > 0 && pokemonCP >= 700)
{
ColoredConsoleWrite(ConsoleColor.Green, $"Master Ball is being used");
return MiscEnums.Item.ITEM_MASTER_BALL;
}
return MiscEnums.Item.ITEM_POKE_BALL;
}
public static void ColoredConsoleWrite(ConsoleColor color, string text)
{
ConsoleColor originalColor = System.Console.ForegroundColor;
System.Console.ForegroundColor = color;
System.Console.WriteLine(text);
System.Console.ForegroundColor = originalColor;
if (OnConsoleWrite != null)
{
OnConsoleWrite(color, text);
}
}
public async Task<FortDetailsResponse> GetFort(string fortId, double fortLat, double fortLng)
{
var customRequest = new Request.Types.FortDetailsRequest
{
Id = ByteString.CopyFromUtf8(fortId),
Latitude = Utils.FloatAsUlong(fortLat),
Longitude = Utils.FloatAsUlong(fortLng)
};
var fortDetailRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 10,
new Request.Types.Requests
{
Type = (int)RequestType.FORT_DETAILS,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, FortDetailsResponse>($"https://{_apiUrl}/rpc",
fortDetailRequest);
}
public async Task<GetInventoryResponse> GetInventory()
{
var inventoryRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
RequestType.GET_INVENTORY);
return
await
_httpClient.PostProtoPayload<Request, GetInventoryResponse>($"https://{_apiUrl}/rpc",
inventoryRequest);
}
public async Task<GetMapObjectsResponse> GetMapObjects()
{
var customRequest = new Request.Types.MapObjectsRequest
{
CellIds =
ByteString.CopyFrom(
ProtoHelper.EncodeUlongList(S2Helper.GetNearbyCellIds(_currentLng,
_currentLat))),
Latitude = Utils.FloatAsUlong(_currentLat),
Longitude = Utils.FloatAsUlong(_currentLng),
Unknown14 = ByteString.CopyFromUtf8("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
};
var mapRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 10,
new Request.Types.Requests
{
Type = (int)RequestType.GET_MAP_OBJECTS,
Message = customRequest.ToByteString()
},
new Request.Types.Requests { Type = (int)RequestType.GET_HATCHED_OBJECTS },
new Request.Types.Requests
{
Type = (int)RequestType.GET_INVENTORY,
Message = new Request.Types.Time { Time_ = DateTime.UtcNow.ToUnixTime() }.ToByteString()
},
new Request.Types.Requests { Type = (int)RequestType.CHECK_AWARDED_BADGES },
new Request.Types.Requests
{
Type = (int)RequestType.DOWNLOAD_SETTINGS,
Message =
new Request.Types.SettingsGuid
{
Guid = ByteString.CopyFromUtf8("4a2e9bc330dae60e7b74fc85b98868ab4700802e")
}.ToByteString()
});
return
await _httpClient.PostProtoPayload<Request, GetMapObjectsResponse>($"https://{_apiUrl}/rpc", mapRequest);
}
public async Task<GetPlayerResponse> GetProfile()
{
var profileRequest = RequestBuilder.GetInitialRequest(_accessToken, _settings.AuthType, _currentLat, _currentLng, 10,
new Request.Types.Requests { Type = (int)RequestType.GET_PLAYER });
return
await _httpClient.PostProtoPayload<Request, GetPlayerResponse>($"https://{_apiUrl}/rpc", profileRequest);
}
public async Task<DownloadSettingsResponse> GetSettings()
{
var settingsRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 10,
RequestType.DOWNLOAD_SETTINGS);
return
await
_httpClient.PostProtoPayload<Request, DownloadSettingsResponse>($"https://{_apiUrl}/rpc",
settingsRequest);
}
/*num Holoholo.Rpc.Types.FortSearchOutProto.Result {
NO_RESULT_SET = 0;
SUCCESS = 1;
OUT_OF_RANGE = 2;
IN_COOLDOWN_PERIOD = 3;
INVENTORY_FULL = 4;
}*/
public async Task<FortSearchResponse> SearchFort(string fortId, double fortLat, double fortLng)
{
var customRequest = new Request.Types.FortSearchRequest
{
Id = ByteString.CopyFromUtf8(fortId),
FortLatDegrees = Utils.FloatAsUlong(fortLat),
FortLngDegrees = Utils.FloatAsUlong(fortLng),
PlayerLatDegrees = Utils.FloatAsUlong(_currentLat),
PlayerLngDegrees = Utils.FloatAsUlong(_currentLng)
};
var fortDetailRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.FORT_SEARCH,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, FortSearchResponse>($"https://{_apiUrl}/rpc",
fortDetailRequest);
}
private void SetCoordinates(double lat, double lng)
{
_currentLat = lat;
_currentLng = lng;
// _settings.DefaultLatitude = lat;
// _settings.DefaultLongitude = lng;
}
public async Task SetServer()
{
var serverRequest = RequestBuilder.GetInitialRequest(_accessToken, _settings.AuthType, _currentLat, _currentLng, 10,
RequestType.GET_PLAYER, RequestType.GET_HATCHED_OBJECTS, RequestType.GET_INVENTORY,
RequestType.CHECK_AWARDED_BADGES, RequestType.DOWNLOAD_SETTINGS);
var serverResponse = await _httpClient.PostProto(Resources.RpcUrl, serverRequest);
_unknownAuth = new Request.Types.UnknownAuth
{
Unknown71 = serverResponse.Auth.Unknown71,
Timestamp = serverResponse.Auth.Timestamp,
Unknown73 = serverResponse.Auth.Unknown73
};
_apiUrl = serverResponse.ApiUrl;
}
public async Task<TransferPokemonOut> TransferPokemon(ulong pokemonId)
{
var customRequest = new TransferPokemon
{
PokemonId = pokemonId
};
var releasePokemonRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.RELEASE_POKEMON,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, TransferPokemonOut>($"https://{_apiUrl}/rpc",
releasePokemonRequest);
}
public double CurrentLatitude
{
get
{
return this._currentLat;
}
}
public double CurrentLongitude
{
get
{
return this._currentLng;
}
}
public async Task<PlayerUpdateResponse> UpdatePlayerLocation(double lat, double lng)
{
SetCoordinates(lat, lng);
var latlng = _currentLat + ":" + _currentLng;
File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "coords.txt", latlng);
var customRequest = new Request.Types.PlayerUpdateProto
{
Lat = Utils.FloatAsUlong(_currentLat),
Lng = Utils.FloatAsUlong(_currentLng)
};
var updateRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 10,
new Request.Types.Requests
{
Type = (int)RequestType.PLAYER_UPDATE,
Message = customRequest.ToByteString()
});
var updateResponse =
await
_httpClient.PostProtoPayload<Request, PlayerUpdateResponse>($"https://{_apiUrl}/rpc", updateRequest);
return updateResponse;
}
public async Task<IEnumerable<Item>> GetItemsToRecycle(ISettings settings, Client client)
{
var myItems = await GetItems(client);
return myItems
.Where(x => settings.ItemRecycleFilter.Any(f => f.Key == ((ItemId)x.Item_) && x.Count > f.Value))
.Select(x => new Item { Item_ = x.Item_, Count = x.Count - settings.ItemRecycleFilter.Single(f => f.Key == (AllEnum.ItemId)x.Item_).Value, Unseen = x.Unseen });
}
public async Task RecycleItems(Client client)
{
var items = await GetItemsToRecycle(_settings, client);
foreach (var item in items)
{
var transfer = await RecycleItem((AllEnum.ItemId)item.Item_, item.Count);
ColoredConsoleWrite(ConsoleColor.DarkCyan, $"Recycled {item.Count}x {((AllEnum.ItemId)item.Item_).ToString().Substring(4)}");
await Task.Delay(500);
}
await Task.Delay(_settings.RecycleItemsInterval * 1000);
RecycleItems(client);
}
public async Task<Response.Types.Unknown6> RecycleItem(AllEnum.ItemId itemId, int amount)
{
var customRequest = new InventoryItemData.RecycleInventoryItem
{
ItemId = (AllEnum.ItemId)Enum.Parse(typeof(AllEnum.ItemId), itemId.ToString()),
Count = amount
};
var releasePokemonRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests()
{
Type = (int)RequestType.RECYCLE_INVENTORY_ITEM,
Message = customRequest.ToByteString()
});
return await _httpClient.PostProtoPayload<Request, Response.Types.Unknown6>($"https://{_apiUrl}/rpc", releasePokemonRequest);
}
public async Task<IEnumerable<Item>> GetItems(Client client)
{
var inventory = await client.GetInventory();
return inventory.InventoryDelta.InventoryItems
.Select(i => i.InventoryItemData?.Item)
.Where(p => p != null);
}
public async Task<UseItemCaptureRequest> UseCaptureItem(ulong encounterId, AllEnum.ItemId itemId, string spawnPointGuid)
{
var customRequest = new UseItemCaptureRequest
{
EncounterId = encounterId,
ItemId = itemId,
SpawnPointGuid = spawnPointGuid
};
var useItemRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests()
{
Type = (int)RequestType.USE_ITEM_CAPTURE,
Message = customRequest.ToByteString()
});
return await _httpClient.PostProtoPayload<Request, UseItemCaptureRequest>($"https://{_apiUrl}/rpc", useItemRequest);
}
public async Task UseRazzBerry(Client client, ulong encounterId, string spawnPointGuid)
{
IEnumerable<Item> myItems = await GetItems(client);
IEnumerable<Item> RazzBerries = myItems.Where(i => (ItemId)i.Item_ == ItemId.ItemRazzBerry);
Item RazzBerry = RazzBerries.FirstOrDefault();
if (RazzBerry != null)
{
UseItemCaptureRequest useRazzBerry = await client.UseCaptureItem(encounterId, AllEnum.ItemId.ItemRazzBerry, spawnPointGuid);
ColoredConsoleWrite(ConsoleColor.Green, $"Using a Razz Berry, we have {RazzBerry.Count} left");
await Task.Delay(2000);
}
else
{
ColoredConsoleWrite(ConsoleColor.Red, $"You don't have any Razz Berry to use.");
}
}
public async Task<UseItemRequest> UseItemXpBoost(ItemId itemId)
{
var customRequest = new UseItemRequest
{
ItemId = itemId,
};
var useItemRequest = RequestBuilder.GetRequest(_unknownAuth, _currentLat, _currentLng, 30,
new Request.Types.Requests
{
Type = (int)RequestType.USE_ITEM_XP_BOOST,
Message = customRequest.ToByteString()
});
return
await
_httpClient.PostProtoPayload<Request, UseItemRequest>($"https://{_apiUrl}/rpc",
useItemRequest);
}
}
}
You may download the files in Public Git.