def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None): if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username is not None and password is not None: self._auth_provider.user_login(username, password) else: raise AuthException( "Invalid Credential Input - Please provide username/password or an oauth2 refresh token" )
def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None, proxy_config=None, user_agent=None, timeout=None): if provider == 'ptc': self._auth_provider = AuthPtc(user_agent=user_agent, timeout=timeout) elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise InvalidCredentialsException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: {}'.format(provider)) if proxy_config: self._auth_provider.set_proxy(proxy_config) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username and password: if not self._auth_provider.user_login(username, password): raise AuthException("User login failed!") else: raise InvalidCredentialsException( "Invalid Credential Input - Please provide username/password or an oauth2 refresh token" )
def login(self, provider, username, password, lat = None, lng = None, alt = None, app_simulation = True): if (lat is not None) and (lng is not None) and (alt is not None): self._position_lat = lat self._position_lng = lng self._position_alt = alt if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False if app_simulation: self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client request = self.create_request() request.get_player() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = request.call() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if app_simulation: self.log.info('Finished RPC login sequence (app simulation)') else: self.log.info('Finished minimal RPC login sequence') self.log.info('Login process completed') return True
def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance( password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930" ) # not sure what this is but dont change it response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True
def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.debug('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") # not sure what this is but dont change it sleep(3 * random.random() + 5) response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.debug('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') if self.SLOW_BUT_STEALTH: sleep(3 * random.random() + 10) return True
def login(self, provider, username, password, auth_token=None): if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if auth_token is None: if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False with open("token.txt", "w") as f: f.write(self._auth_provider.get_token()) self.log.info('Token saved') else: self.log.info('Reuse token') self._auth_provider._login = True self._auth_provider.set_token(auth_token) self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True
def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None): if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username is not None and password is not None: self._auth_provider.user_login(username, password) else: raise AuthException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token")
def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None, proxy_config=None, user_agent=None, timeout=None): if provider == 'ptc': self._auth_provider = AuthPtc(user_agent=user_agent, timeout=timeout) elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise InvalidCredentialsException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: {}'.format(provider)) if proxy_config: self._auth_provider.set_proxy(proxy_config) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username and password: if not self._auth_provider.user_login(username, password): raise AuthException("User login failed!") else: raise InvalidCredentialsException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token")
class PGoApi: def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None, proxy_config=None, device_info=None): self.set_logger() self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None if provider is not None and ((username is not None and password is not None) or (oauth2_refresh_token is not None)): self.set_authentication(provider, oauth2_refresh_token, username, password, proxy_config) self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") self._position_lat = position_lat self._position_lng = position_lng self._position_alt = position_alt self._hash_server_token = None self._session = requests.session() self._session.headers.update({'User-Agent': 'Niantic App'}) self._session.verify = True if proxy_config is not None: self._session.proxies = proxy_config self.device_info = device_info def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) @staticmethod def get_api_version(): return 9100 def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None, proxy_config=None, user_agent=None, timeout=None): if provider == 'ptc': self._auth_provider = AuthPtc(user_agent=user_agent, timeout=timeout) elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise InvalidCredentialsException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: {}'.format(provider)) if proxy_config: self._auth_provider.set_proxy(proxy_config) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username and password: if not self._auth_provider.user_login(username, password): raise AuthException("User login failed!") else: raise InvalidCredentialsException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token") def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt=None): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def set_proxy(self, proxy_config): self._session.proxies = proxy_config def get_api_endpoint(self): return self._api_endpoint def set_api_endpoint(self, api_url): if api_url.startswith("https"): self._api_endpoint = api_url else: self._api_endpoint = parse_api_endpoint(api_url) def get_auth_provider(self): return self._auth_provider def create_request(self): request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt, self.device_info) return request def activate_hash_server(self, hash_server_token): self._hash_server_token = hash_server_token def get_hash_server_token(self): return self._hash_server_token def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs ) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def app_simulation_login(self): self.log.info('Starting RPC login sequence (iOS app simulation)') # Send empty initial request request = self.create_request() response = request.call() time.sleep(1.5) # Send GET_PLAYER only request = self.create_request() request.get_player(player_locale = {'country': 'US', 'language': 'en', 'timezone': 'America/Chicago'}) response = request.call() if response.get('responses', {}).get('GET_PLAYER', {}).get('banned', False): raise BannedAccountException time.sleep(1.5) request = self.create_request() request.download_remote_config_version(platform=1, app_version=self.get_api_version()) request.check_challenge() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings() response = request.call() self.log.info('Finished RPC login sequence (iOS app simulation)') return response """ The login function is not needed anymore but still in the code for backward compatibility" """ def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): if lat and lng: self._position_lat = lat self._position_lng = lng if alt: self._position_alt = alt try: self.set_authentication(provider, username=username, password=password) except AuthException as e: self.log.error('Login process failed: %s', e) return False if app_simulation: response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self.config = config self._position_lat = 0 self._position_lng = 0 self._position_alt = 0 self._posf = (0,0,0) self.MIN_KEEP_IV = config.get("MIN_KEEP_IV", 0) self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) self.RELEASE_DUPLICATES = config.get("RELEASE_DUPLICATE", 0) self.DUPLICATE_CP_FORGIVENESS = config.get("DUPLICATE_CP_FOREGIVENESS", 0) self._req_method_list = [] self._heartbeat_number = 5 self.pokemon_names = pokemon_names def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i),i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat,lng,alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append( { RequestType.Value(name): kwargs } ) self.log.debug("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append( RequestType.Value(name) ) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def heartbeat(self): self.get_player() if self._heartbeat_number % 10 == 0: self.check_awarded_badges() self.get_inventory() res = self.call() if res.get("direction",-1) == 102: self.log.error("There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format(json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) currencies = player_data.get('currencies', []) currency_data = ",".join(map(lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) self.log.info("Username: %s, Currencies: %s", player_data.get('username', 'NA'), currency_data) if 'GET_INVENTORY' in res['responses']: with open("accounts/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) self.log.info("List of Pokemon:\n" + get_inventory_data(res, self.pokemon_names) + "\nTotal Pokemon count: " + str(get_pokemon_num(res))) self.log.debug(self.cleanup_inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'])) self._heartbeat_number += 1 return res def walk_to(self,loc): steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", "")) for step in steps: for i,next_point in enumerate(get_increments(self._posf,step,self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() self.log.info("Sleeping before next heartbeat") sleep(2) # If you want to make it faster, delete this line... would not recommend though while self.catch_near_pokemon(): sleep(1) # If you want to make it faster, delete this line... would not recommend though def spin_near_fort(self): map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._posf,forts) if destinations: fort = destinations[0] self.log.info("Walking to fort at %s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) position = self._posf # FIXME ? res = self.fort_search(fort_id = fort['id'], fort_latitude=fort['latitude'],fort_longitude=fort['longitude'],player_latitude=position[0],player_longitude=position[1]).call()['responses']['FORT_SEARCH'] self.log.debug("Fort spinned: %s", res) if 'lure_info' in fort: encounter_id = fort['lure_info']['encounter_id'] fort_id = fort['lure_info']['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] self.disk_encounter_pokemon(fort['lure_info']) return True else: self.log.error("No fort to walk to!") return False def catch_near_pokemon(self): map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [(pokemon, distance_in_meters(origin,(pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons] self.log.debug("Nearby pokemon: : %s", pokemon_distances) for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) self.log.info("Catching Pokemon: %s", self.pokemon_names[str(target[0]['pokemon_id'])]) return self.encounter_pokemon(target[0]) return False def nearby_map_objects(self): position = self.get_position() neighbors = getNeighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0]*len(neighbors), cell_id=neighbors).call() def attempt_catch(self,encounter_id,spawn_point_guid): #Problem here... add 4 if you have master ball for i in range(1,3): # Range 1...4 iff you have master ball `range(1,4)` r = self.catch_pokemon( normalized_reticle_size= 1.950, pokeball = i, spin_modifier= 0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_guid=spawn_point_guid, ).call()['responses']['CATCH_POKEMON'] if "status" in r: return r def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] all_actual_items = [xiq['inventory_item_data']["item"] for xiq in inventory_items if "item" in xiq['inventory_item_data']] all_actual_item_str = "List of items:\n" all_actual_item_count = 0 all_actual_items = sorted([x for x in all_actual_items if "count" in x], key=lambda x: x["item_id"]) for xiq in all_actual_items: true_item_name = INVENTORY_DICT[xiq["item_id"]] all_actual_item_str += "Item_ID "+str(xiq["item_id"])+"\titem count "+str(xiq["count"])+"\t("+true_item_name+")\n" all_actual_item_count += xiq["count"] all_actual_item_str += "Total item count: "+str(all_actual_item_count) self.log.info(all_actual_item_str) caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = inventory_item['inventory_item_data']['pokemon_data'] if 'cp' in pokemon and "favorite" not in pokemon: caught_pokemon[pokemon["pokemon_id"]].append(pokemon) elif "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in MIN_BAD_ITEM_COUNTS and "count" in item and item['count'] > MIN_BAD_ITEM_COUNTS[item['item_id']]: recycle_count = item['count'] - MIN_BAD_ITEM_COUNTS[item['item_id']] self.log.info("Recycling Item_ID {0}, item count {1}".format(item['item_id'], recycle_count)) self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count) for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, lambda x,y: cmp(x['cp'],y['cp']),reverse=True) for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if 'cp' in pokemon and pokemonIVPercentage(pokemon) < self.MIN_KEEP_IV and pokemon['cp'] < self.KEEP_CP_OVER: if pokemon['pokemon_id'] == 16: for inventory_item in inventory_items: if "pokemon_family" in inventory_item['inventory_item_data'] and inventory_item['inventory_item_data']['pokemon_family']['family_id'] == 16 and inventory_item['inventory_item_data']['pokemon_family']['candy'] > 11: self.log.info("Evolving pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self.evolve_pokemon(pokemon_id = pokemon['id']) self.log.debug("Releasing pokemon: %s", pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(pokemon['pokemon_id'])], pokemonIVPercentage(pokemon)) self.release_pokemon(pokemon_id = pokemon["id"]) if self.RELEASE_DUPLICATES: for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, lambda x,y: cmp(self.pokemon_names[str(x['pokemon_id'])], self.pokemon_names[str(y['pokemon_id'])])) last_pokemon = pokemons[0] for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if self.pokemon_names[str(pokemon['pokemon_id'])] == self.pokemon_names[str(last_pokemon['pokemon_id'])]: # two of the same pokemon, compare and release smaller of the two if pokemon['cp'] > last_pokemon['cp']: if pokemon['cp'] * self.DUPLICATE_CP_FORGIVENESS > last_pokemon['cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", last_pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(last_pokemon['pokemon_id'])], pokemonIVPercentage(pokemon)) self.release_pokemon(pokemon_id = last_pokemon["id"]) last_pokemon = pokemon else: if last_pokemon['cp'] * self.DUPLICATE_CP_FORGIVENESS > pokemon['cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(pokemon['pokemon_id'])], pokemonIVPercentage(pokemon)) self.release_pokemon(pokemon_id = pokemon["id"]) else: last_pokemon = pokemon return self.call() def disk_encounter_pokemon(self, lureinfo): try: encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] if resp['result'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id,fort_id) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info("Caught Pokemon: %s", self.pokemon_names[str(resp['pokemon_data']['pokemon_id'])]) sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info("Failed to catch Pokemon: %s", self.pokemon_names[str(resp['pokemon_data']['pokemon_id'])]) return False sleep(2) # If you want to make it faster, delete this line... would not recommend though except Exception as e: self.log.error("Error in disk encounter %s", e) return False def encounter_pokemon(self,pokemon): encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] position = self._posf encounter = self.encounter(encounter_id=encounter_id,spawn_point_id=spawn_point_id,player_latitude=position[0],player_longitude=position[1]).call()['responses']['ENCOUNTER'] self.log.debug("Started Encounter: %s", encounter) if encounter['status'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id,spawn_point_id) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info("Caught Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info("Failed to Catch Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) return False sleep(2) # If you want to make it faster, delete this line... would not recommend though return False def login(self, provider, username, password,cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname,"w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): self.heartbeat() while True: self.heartbeat() sleep(1) # If you want to make it faster, delete this line... would not recommend though self.spin_near_fort() while self.catch_near_pokemon(): sleep(4) # If you want to make it faster, delete this line... would not recommend though pass @staticmethod def flatmap(f, items): return chain.from_iterable(imap(f, items))
class PGoApi: def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None, proxy_config=None, device_info=None): self.set_logger() self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None if provider is not None and ( (username is not None and password is not None) or (oauth2_refresh_token is not None)): self.set_authentication(provider, oauth2_refresh_token, username, password, proxy_config) self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") self._position_lat = position_lat self._position_lng = position_lng self._position_alt = position_alt self._hash_server_token = None self._session = requests.session() self._session.headers.update({'User-Agent': 'Niantic App'}) self._session.verify = True if proxy_config is not None: self._session.proxies = proxy_config self.device_info = device_info def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) @staticmethod def get_api_version(): return 9100 def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None, proxy_config=None, user_agent=None, timeout=None): if provider == 'ptc': self._auth_provider = AuthPtc(user_agent=user_agent, timeout=timeout) elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise InvalidCredentialsException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: {}'.format(provider)) if proxy_config: self._auth_provider.set_proxy(proxy_config) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username and password: if not self._auth_provider.user_login(username, password): raise AuthException("User login failed!") else: raise InvalidCredentialsException( "Invalid Credential Input - Please provide username/password or an oauth2 refresh token" ) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt=None): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def set_proxy(self, proxy_config): self._session.proxies = proxy_config def get_api_endpoint(self): return self._api_endpoint def set_api_endpoint(self, api_url): if api_url.startswith("https"): self._api_endpoint = api_url else: self._api_endpoint = parse_api_endpoint(api_url) def get_auth_provider(self): return self._auth_provider def create_request(self): request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt, self.device_info) return request def activate_hash_server(self, hash_server_token): self._hash_server_token = hash_server_token def get_hash_server_token(self): return self._hash_server_token def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def app_simulation_login(self): self.log.info('Starting RPC login sequence (iOS app simulation)') # Send empty initial request request = self.create_request() response = request.call() time.sleep(1.5) # Send GET_PLAYER only request = self.create_request() request.get_player(player_locale={ 'country': 'US', 'language': 'en', 'timezone': 'America/Chicago' }) response = request.call() if response.get('responses', {}).get('GET_PLAYER', {}).get('banned', False): raise BannedAccountException time.sleep(1.5) request = self.create_request() request.download_remote_config_version( platform=1, app_version=self.get_api_version()) request.check_challenge() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings() response = request.call() self.log.info('Finished RPC login sequence (iOS app simulation)') return response """ The login function is not needed anymore but still in the code for backward compatibility" """ def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): if lat and lng: self._position_lat = lat self._position_lng = lng if alt: self._position_alt = alt try: self.set_authentication(provider, username=username, password=password) except AuthException as e: self.log.error('Login process failed: %s', e) return False if app_simulation: response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False self.log.info('Login process completed') return True
class PGoApi: def __init__(self): self.set_logger() self._auth_provider = None self._api_endpoint = 'https://pgorelease.nianticlabs.com/plfe/rpc' self._position_lat = None self._position_lng = None self._position_alt = None self.log.info('%s v%s - %s', __title__, __version__, __copyright__ ) def set_logger(self, logger = None): self.log = logger or logging.getLogger(__name__) def get_api_endpoint(self): return self._api_endpoint def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def create_request(self): request = PGoApiRequest(self._api_endpoint, self._auth_provider, self._position_lat, self._position_lng, self._position_alt) return request def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)( _call_direct = True, **kwargs ) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def login(self, provider, username, password, lat = None, lng = None, alt = None, app_simulation = True): if (lat is not None) and (lng is not None) and (alt is not None): self._position_lat = lat self._position_lng = lng self._position_alt = alt if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False if app_simulation: self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client request = self.create_request() request.get_player() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = request.call() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if app_simulation: self.log.info('Finished RPC login sequence (app simulation)') else: self.log.info('Finished minimal RPC login sequence') self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self.config = config self._position_lat = 0 # int cooords self._position_lng = 0 self._position_alt = 0 self._posf = (0, 0, 0) # this is floats self._origPosF = (0, 0, 0) # this is original position in floats self._req_method_list = [] self._heartbeat_number = 5 self._firstRun = True self.pokemon_caught = 0 self.inventory = Player_Inventory([]) self.pokemon_names = pokemon_names self.MIN_ITEMS = {} for k, v in config.get("MIN_ITEMS", {}).items(): self.MIN_ITEMS[getattr(Inventory, k)] = v self.POKEMON_EVOLUTION = {} self.POKEMON_EVOLUTION_FAMILY = {} for k, v in config.get("POKEMON_EVOLUTION", {}).items(): self.POKEMON_EVOLUTION[getattr(Enums_pb2, k)] = v self.POKEMON_EVOLUTION_FAMILY[getattr(Enums_pb2, k)] = getattr(Enums_pb2, "FAMILY_" + k) self.MIN_KEEP_IV = config.get("MIN_KEEP_IV", 0) # release anything under this if we don't have it already self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) # release anything under this if we don't have it already self.MIN_SIMILAR_POKEMON = config.get("MIN_SIMILAR_POKEMON", 1) # Keep atleast one of everything. self.STAY_WITHIN_PROXIMITY = config.get("STAY_WITHIN_PROXIMITY", False) # Stay within proximity self.visited_forts = ExpiringDict(max_len=120, max_age_seconds=config.get("SKIP_VISITED_FORT_DURATION", 600)) self.experimental = config.get("EXPERIMENTAL", False) self.spin_all_forts = config.get("SPIN_ALL_FORTS", False) self.keep_pokemon_ids = map(lambda x: getattr(Enums_pb2, x), config.get("KEEP_POKEMON_NAMES", [])) self.throw_pokemon_ids = map(lambda x: getattr(Enums_pb2, x), config.get("THROW_POKEMON_NAMES", [])) self.max_catch_attempts = config.get("MAX_CATCH_ATTEMPTS", 10) self.game_master = parse_game_master() def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) if self._firstRun: self._firstRun = False self._origPosF = self._posf self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.debug("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def update_player_inventory(self): self.get_inventory() res = self.call() if 'GET_INVENTORY' in res['responses']: self.inventory = Player_Inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']) self.log.info("Player Items: %s", self.inventory) def heartbeat(self): # making a standard call to update position, etc self.get_player() if self._heartbeat_number % 10 == 0: self.check_awarded_badges() self.get_inventory() # self.download_settings(hash="4a2e9bc330dae60e7b74fc85b98868ab4700802e") res = self.call() if not res or res.get("direction", -1) == 102: self.log.error("There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format(json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) currencies = player_data.get('currencies', []) currency_data = ",".join( map(lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) self.log.info("Username: %s, Currencies: %s, Pokemon Caught in this run: %s", player_data.get('username', 'NA'), currency_data, self.pokemon_caught) if 'GET_INVENTORY' in res['responses']: with open("data_dumps/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) self.log.info(get_inventory_data(res, self.pokemon_names)) self.log.info("Player Items: %s", self.inventory) self.inventory = Player_Inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']) self.log.debug(self.cleanup_inventory(self.inventory.inventory_items)) self.attempt_evolve(self.inventory.inventory_items) self.cleanup_pokemon(self.inventory.inventory_items) self._heartbeat_number += 1 return res def walk_to(self, loc, waypoints=[]): # location in floats of course... steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", ""), self.experimental and self.spin_all_forts, waypoints) catch_attempt = 0 for step in steps: for i, next_point in enumerate(get_increments(self._posf, step, self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() if self.experimental and self.spin_all_forts: self.spin_nearest_fort() self.log.info("On my way to the next fort! :)") sleep(1) while self.catch_near_pokemon() and catch_attempt <= self.max_catch_attempts: sleep(1) catch_attempt += 1 catch_attempt = 0 def walk_back_to_origin(self): self.walk_to(self._origPosF) def spin_nearest_fort(self): map_cells = self.nearby_map_objects()['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts) if destinations: nearest_fort = destinations[0][0] nearest_fort_dis = destinations[0][1] if nearest_fort_dis <= 40.00: self.fort_search_pgoapi(nearest_fort, player_postion=self.get_position(), fort_distance=nearest_fort_dis) if 'lure_info' in nearest_fort: self.disk_encounter_pokemon(nearest_fort['lure_info']) else: self.log.info('No spinnable forts within proximity. Or server returned no map objects.') def fort_search_pgoapi(self, fort, player_postion, fort_distance): res = self.fort_search(fort_id=fort['id'], fort_latitude=fort['latitude'], fort_longitude=fort['longitude'], player_latitude=player_postion[0], player_longitude=player_postion[1]).call()['responses']['FORT_SEARCH'] if res['result'] == 1: self.log.debug("Fort spinned: %s", res) self.log.info("Fort Spinned: http://maps.google.com/maps?q=%s,%s", fort['latitude'], fort['longitude']) self.visited_forts[fort['id']] = fort elif res['result'] == 4: self.log.debug("For spinned but Your inventory is full : %s", res) self.log.info("For spinned but Your inventory is full.") self.visited_forts[fort['id']] = fort elif res['result'] == 2: self.log.debug("Could not spin fort - fort not in range %s", res) self.log.info("Could not spin fort http://maps.google.com/maps?q=%s,%s, Not in Range %s", fort['latitude'], fort['longitude'], fort_distance) else: self.log.debug("Could not spin fort %s", res) self.log.info("Could not spin fort http://maps.google.com/maps?q=%s,%s, Error id: %s", fort['latitude'], fort['longitude'], res['result']) return False return True def spin_all_forts_visible(self): res = self.nearby_map_objects() map_cells = res['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts, self.experimental) if not destinations: self.log.debug("No fort to walk to! %s", res) self.log.info('No more spinnable forts within proximity. Or server error') self.walk_back_to_origin() return False if len(destinations) >= 20: destinations = destinations[:20] furthest_fort = destinations[0][0] self.log.info("Walking to fort at http://maps.google.com/maps?q=%s,%s", furthest_fort['latitude'], furthest_fort['longitude']) self.walk_to((furthest_fort['latitude'], furthest_fort['longitude']), map(lambda x: "via:%f,%f" % (x[0]['latitude'], x[0]['longitude']), destinations[1:])) return True def return_to_start(self): self.set_position(*self._origPosF) def spin_near_fort(self): res = self.nearby_map_objects() map_cells = res['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts, self.experimental) if not destinations: self.log.debug("No fort to walk to! %s", res) self.log.info('No more spinnable forts within proximity. Returning back to origin') self.walk_back_to_origin() return False for fort_data in destinations: fort = fort_data[0] self.log.info("Walking to fort at http://maps.google.com/maps?q=%s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) self.fort_search_pgoapi(fort, self.get_position(), fort_data[1]) if 'lure_info' in fort: self.disk_encounter_pokemon(fort['lure_info']) return True def catch_near_pokemon(self): map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [(pokemon, distance_in_meters(origin, (pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons] if pokemons: self.log.debug("Nearby pokemon: : %s", pokemon_distances) self.log.info("Nearby Pokemon: %s", ", ".join(map(lambda x: self.pokemon_names[str(x['pokemon_id'])], pokemons))) else: self.log.info("No nearby pokemon") catches_successful = False for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) self.log.info("Catching Pokemon: %s", self.pokemon_names[str(target[0]['pokemon_id'])]) catches_successful &= self.encounter_pokemon(target[0]) sleep(random.randrange(4, 8)) return catches_successful def nearby_map_objects(self): position = self.get_position() neighbors = getNeighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0] * len(neighbors), cell_id=neighbors).call() def attempt_catch(self, encounter_id, spawn_point_guid): catch_status = -1 catch_attempts = 0 ret = {} # Max 4 attempts to catch pokemon while catch_status != 1 and self.inventory.can_attempt_catch() and catch_attempts < 5: pokeball = self.inventory.take_next_ball() r = self.catch_pokemon( normalized_reticle_size=1.950, pokeball=pokeball, spin_modifier=0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_guid=spawn_point_guid, ).call()['responses']['CATCH_POKEMON'] catch_attempts += 1 if "status" in r: catch_status = r['status'] # fleed or error if catch_status == 3 or catch_status == 0: break ret = r if 'status' in ret: return ret return {} def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] for inventory_item in inventory_items: if "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in self.MIN_ITEMS and "count" in item and item['count'] > self.MIN_ITEMS[ item['item_id']]: recycle_count = item['count'] - self.MIN_ITEMS[item['item_id']] self.log.info("Recycling Item_ID {0}, item count {1}".format(item['item_id'], recycle_count)) res = self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count).call()['responses'][ 'RECYCLE_INVENTORY_ITEM'] response_code = res['result'] if response_code == 1: self.log.info("Recycled Item %s, New Count: %s", item['item_id'], res.get('new_count', 0)) else: self.log.info("Failed to recycle Item %s, Code: %s", item['item_id'], response_code) sleep(2) return self.update_player_inventory() def get_caught_pokemons(self, inventory_items): caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = Pokemon(inventory_item['inventory_item_data']['pokemon_data'], self.pokemon_names) pokemon.pokemon_additional_data = self.game_master.get(pokemon.pokemon_id, PokemonData()) if not pokemon.is_egg: caught_pokemon[pokemon.pokemon_id].append(pokemon) return caught_pokemon def cleanup_pokemon(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] caught_pokemon = self.get_caught_pokemons(inventory_items) for pokemons in caught_pokemon.values(): # Only if we have more than MIN_SIMILAR_POKEMON if len(pokemons) > self.MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, key=lambda x: (x.cp, x.iv), reverse=True) # keep the first pokemon.... for pokemon in pokemons[self.MIN_SIMILAR_POKEMON:]: if self.is_pokemon_eligible_for_transfer(pokemon): self.log.info("Releasing pokemon: %s", pokemon) self.release_pokemon(pokemon_id=pokemon.id) release_res = self.call()['responses']['RELEASE_POKEMON'] status = release_res.get('result', -1) if status == 1: self.log.info("Successfully Released Pokemon %s", pokemon) else: self.log.debug("Failed to release pokemon %s, %s", pokemon, release_res) self.log.info("Failed to release Pokemon %s", pokemon) sleep(3) def is_pokemon_eligible_for_transfer(self, pokemon): return (pokemon.pokemon_id in self.throw_pokemon_ids and not pokemon.is_favorite) \ or (not pokemon.is_favorite and pokemon.iv < self.MIN_KEEP_IV and pokemon.cp < self.KEEP_CP_OVER and pokemon.is_valid_pokemon() and pokemon.pokemon_id not in self.keep_pokemon_ids) def attempt_evolve(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] caught_pokemon = self.get_caught_pokemons(inventory_items) self.inventory = Player_Inventory(inventory_items) for pokemons in caught_pokemon.values(): if len(pokemons) > self.MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, key=lambda x: (x.cp, x.iv), reverse=True) for pokemon in pokemons[self.MIN_SIMILAR_POKEMON:]: # If we can't evolve this type of pokemon anymore, don't check others. if not self.attempt_evolve_pokemon(pokemon): break return False def attempt_evolve_pokemon(self, pokemon): if self.is_pokemon_eligible_for_evolution(pokemon=pokemon): self.log.info("Evolving pokemon: %s", pokemon) evo_res = self.evolve_pokemon(pokemon_id=pokemon.id).call()['responses']['EVOLVE_POKEMON'] status = evo_res.get('result', -1) sleep(3) if status == 1: evolved_pokemon = Pokemon(evo_res.get('evolved_pokemon_data', {}), self.pokemon_names, self.game_master.get(str(pokemon.pokemon_id), PokemonData())) # I don' think we need additional stats for evolved pokemon. Since we do not do anything with it. # evolved_pokemon.pokemon_additional_data = self.game_master.get(pokemon.pokemon_id, PokemonData()) self.log.info("Evolved to %s", evolved_pokemon) self.update_player_inventory() return True else: self.log.debug("Could not evolve Pokemon %s", evo_res) self.log.info("Could not evolve pokemon %s | Status %s", pokemon, status) self.update_player_inventory() return False else: return False def is_pokemon_eligible_for_evolution(self, pokemon): return self.inventory.pokemon_candy.get(self.POKEMON_EVOLUTION_FAMILY.get(pokemon.pokemon_id, None), -1) > self.POKEMON_EVOLUTION.get(pokemon.pokemon_id, None) \ and pokemon.pokemon_id not in self.keep_pokemon_ids \ and not pokemon.is_favorite \ and pokemon.pokemon_id in self.POKEMON_EVOLUTION def disk_encounter_pokemon(self, lureinfo, retry=False): try: self.update_player_inventory() if not self.inventory.can_attempt_catch(): self.log.info("No balls to catch %s, exiting disk encounter", self.inventory) return False encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf self.log.debug("At Fort with lure %s".encode('ascii', 'ignore'), lureinfo) self.log.info("At Fort with Lure AND Active Pokemon %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] if resp['result'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id, fort_id) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("(LURE) Caught Pokemon: : %s", catch_attempt) self.log.info("(LURE) Caught Pokemon: %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) self.pokemon_caught += 1 sleep(2) return True elif capture_status != 2: self.log.debug("(LURE) Failed Catch: : %s", catch_attempt) self.log.info("(LURE) Failed to catch Pokemon: %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) return False sleep(2) elif resp['result'] == 5: self.log.info("Couldn't catch %s Your pokemon bag was full, attempting to clear and re-try", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) self.cleanup_pokemon() if not retry: return self.disk_encounter_pokemon(lureinfo, retry=True) else: self.log.info("Could not start Disk (lure) encounter for pokemon: %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) except Exception as e: self.log.error("Error in disk encounter %s", e) return False def encounter_pokemon(self, pokemon, retry=False): # take in a MapPokemon from MapCell.catchable_pokemons # Update Inventory to make sure we can catch this mon self.update_player_inventory() if not self.inventory.can_attempt_catch(): self.log.info("No balls to catch %s, exiting encounter", self.inventory) return False encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] # begin encounter_id position = self.get_position() encounter = self.encounter(encounter_id=encounter_id, spawn_point_id=spawn_point_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['ENCOUNTER'] self.log.debug("Attempting to Start Encounter: %s", encounter) if encounter['status'] == 1: capture_status = -1 # while capture_status != RpcEnum.CATCH_ERROR and capture_status != RpcEnum.CATCH_FLEE: while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id, spawn_point_id) capture_status = catch_attempt.get('status', -1) # if status == RpcEnum.CATCH_SUCCESS: if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info("Caught Pokemon: %s", self.pokemon_names.get(str(pokemon['pokemon_id']), "NA")) self.pokemon_caught += 1 sleep(2) return True elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info("Failed to Catch Pokemon: %s", self.pokemon_names.get(str(pokemon['pokemon_id']), "NA")) return False sleep(2) elif encounter['status'] == 7: self.log.info("Couldn't catch %s Your pokemon bag was full, attempting to clear and re-try", self.pokemon_names.get(str(pokemon['pokemon_id']), "NA")) self.cleanup_pokemon() if not retry: return self.encounter_pokemon(pokemon, retry=True) else: self.log.info("Could not start encounter for pokemon: %s", self.pokemon_names.get(str(pokemon['pokemon_id']), "NA")) return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): catch_attempt = 0 self.heartbeat() self.cleanup_inventory() while True: self.heartbeat() sleep(1) if self.experimental and self.spin_all_forts: self.spin_all_forts_visible() else: self.spin_near_fort() # if catching fails 10 times, maybe you are sofbanned. while self.catch_near_pokemon() and catch_attempt <= self.max_catch_attempts: sleep(4) catch_attempt += 1 pass if catch_attempt > self.max_catch_attempts: self.log.warn("Your account may be softbaned Or no Pokeballs. Failed to catch pokemon %s times", catch_attempt) catch_attempt = 0 @staticmethod def flatmap(f, items): return list(chain.from_iterable(imap(f, items)))
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_data, start_pos, print_info): self.log = logging.getLogger(__name__) self._start_pos = start_pos self._walk_count = 1 self._auth_provider = None self._api_endpoint = None self.config = config self.set_position(*start_pos) self.RELEASE_DUPLICATES = config.get("RELEASE_DUPLICATE", 0) self._req_method_list = [] self._heartbeat_number = 8 self.pokemon_data = pokemon_data self.do_catch_pokemon = config.get("CATCH_POKEMON", True) self.min_probability_throw = config.get("MIN_PROBABILITY_THROW", 0.5) self.print_info = print_info self.MIN_KEEP_CP = config.get("MIN_KEEP_CP", 1000) def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info(colored('Server seems to be busy or offline - try again!', 'red')) # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i),i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append( { RequestType.Value(name): kwargs } ) self.log.debug("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append( RequestType.Value(name) ) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def heartbeat(self): self.get_player() if self._heartbeat_number % 10 == 0: self.check_awarded_badges() self.get_inventory() res = self.call() if res.get("direction",-1) == 102: self.log.error("There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format(json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) currencies = player_data.get('currencies', []) currency_data = ",".join(map(lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) # self.log.info("Username: %s, Currencies: %s", player_data.get('username', 'NA'), currency_data) if 'GET_INVENTORY' in res['responses']: with open("accounts/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] res['responses']['alt'] = self._posf[2] f.write(json.dumps(res['responses'], indent=2)) if self.print_info: self.log.info(get_inventory_data(res, self.pokemon_data)) self.print_info = False self.log.debug(self.cleanup_inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'])) self._heartbeat_number += 1 return res def walk_to(self,loc): self._walk_count += 1 steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", "")) for step in steps: for i, next_point in enumerate(get_increments(self._posf, step, self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() # self.log.info("Sleeping before next heartbeat") sleep(2) # If you want to make it faster, delete this line... would not recommend though while self.catch_near_pokemon(): sleep(1) # If you want to make it faster, delete this line... would not recommend though def spin_near_fort(self): map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) if self._start_pos and self._walk_count % self.config.get("RETURN_START_INTERVAL") == 0: sleep_time = self.config.get("RETURN_START_INTERVAL") / self.config.get("STEP_SIZE", 200) * 100 self.log.info(colored("Returning home and sleeping for %d seconds", "cyan"), sleep_time) sleep(sleep_time) destinations = filtered_forts(self._start_pos, forts) else: destinations = filtered_forts(self._posf,forts) if destinations: destinationNum = random.randint(0, min(5, len(destinations) - 1)) fort = destinations[destinationNum] self.log.info("Walking to Pokestop at %s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) position = self._posf # FIXME ? res = self.fort_search(fort_id = fort['id'], fort_latitude=fort['latitude'],fort_longitude=fort['longitude'],player_latitude=position[0],player_longitude=position[1]).call()['responses']['FORT_SEARCH'] # self.log.info("Fort spinned: %s", res) if 'lure_info' in fort: encounter_id = fort['lure_info']['encounter_id'] fort_id = fort['lure_info']['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] self.disk_encounter_pokemon(fort['lure_info']) return True else: self.log.error("No Pokestop to walk to!") return False def catch_near_pokemon(self): map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) pokemons = [pokemon for pokemon in pokemons if pokemon['pokemon_id'] not in IGNORE_POKEMON] # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [(pokemon, distance_in_meters(origin,(pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons] self.log.debug("Nearby pokemon: : %s", pokemon_distances) for pokemon_distance in pokemon_distances: target = pokemon_distance return self.encounter_pokemon(target[0]) return False def nearby_map_objects(self): position = self.get_position() neighbors = getNeighbors(self._posf) data = self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0]*len(neighbors), cell_id=neighbors).call() return data def count_pokeballs(self): pokeballs = [0] * 4 inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] for inventory_item in inventory_items: if 'inventory_item_data' in inventory_item: item = inventory_item['inventory_item_data'] if 'item' in item: item_id = item['item']['item_id'] if item_id < 5: pokeballs[item_id-1] += item['item'].get('count') or 1 return pokeballs def attempt_catch(self,encounter_id, spawn_point_guid, encounter): inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] pokeballs = self.count_pokeballs() probabilities = encounter['capture_probability']['capture_probability'] # Throw up to 5 pokeballs at any given pokemon, then give up (our % is pretty damn high) for i in range(1,5): sleep(2) ball_type = 1; while ( (pokeballs[ball_type - 1] == 0) or ((ball_type <= 4) and pokeballs[ball_type] and (probabilities[ball_type - 1] < self.min_probability_throw)) ): ball_type += 1 if pokeballs[ball_type - 1] == 0: self.log.info(colored("No pokeballs to throw, abandoning catch attempt!", "red")) return {'status': 2} pokeballs[ball_type - 1] -= 1 self.log.info('Throwing %s', ['PokeBall', 'GreatBall', 'UltraBall', 'MasterBall'][ball_type - 1]) r = self.catch_pokemon( normalized_reticle_size= 1.950, pokeball = ball_type, spin_modifier= 0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_guid=spawn_point_guid, ).call()['responses'] if 'CATCH_POKEMON' in r: r = r['CATCH_POKEMON'] else: self.log.info(colored("Error catching pokemon: %s", "red"), r) sleep(3) continue if "status" in r: return r # failed to catch return {'status': 3} def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = inventory_item['inventory_item_data']['pokemon_data'] if 'cp' in pokemon and "favorite" not in pokemon: caught_pokemon[pokemon["pokemon_id"]].append(pokemon) elif "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in MIN_BAD_ITEM_COUNTS and "count" in item and item['count'] > MIN_BAD_ITEM_COUNTS[item['item_id']]: recycle_count = item['count'] - MIN_BAD_ITEM_COUNTS[item['item_id']] self.log.info("Recycling Item_ID {0}, item count {1}".format(item['item_id'], recycle_count)) self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count) # evolve the strongest pokemon if possible for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted( pokemons, lambda x,y: cmp(pokemonIVPercentage(x), pokemonIVPercentage(y)) or cmp(x['cp'], y['cp']), reverse=True, ) for pokemon in pokemons[:len(pokemons) - MIN_SIMILAR_POKEMON]: poke_info = self.pokemon_data[str(pokemon['pokemon_id'])] for inventory_item in inventory_items: if ("pokemon_family" in inventory_item['inventory_item_data'] and "evolves_to" in poke_info and inventory_item['inventory_item_data']['pokemon_family']['family_id'] == poke_info['candy_id'] and inventory_item['inventory_item_data']['pokemon_family']['candy'] > ((poke_info.get('max_evolve_candies') or poke_info.get('candies') or 0) + self.config.get("KEEP_CANDIES", 0)) ): self.log.info(colored("Evolving pokemon:", "cyan") + " %s", self.pokemon_data[str(pokemon['pokemon_id'])]['name']) self.evolve_pokemon(pokemon_id = pokemon['id']) caught_pokemon[pokemon["pokemon_id"]].remove(pokemon) # sort and release all weaker pokemon if self.RELEASE_DUPLICATES: for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted( pokemons, lambda x,y: cmp(pokemonIVPercentage(x), pokemonIVPercentage(y)) or cmp(x['cp'], y['cp']), reverse=True, ) for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if pokemon['cp'] < self.MIN_KEEP_CP: self.log.debug("Releasing pokemon: %s", pokemon) self.log.info(colored("Releasing pokemon:", "cyan") + " %s CP: %s, IV: %s", self.pokemon_data[str(pokemon['pokemon_id'])]['name'], str(pokemon['cp']), pokemonIVPercentage(pokemon)) self.release_pokemon(pokemon_id = pokemon["id"]) return self.call() def disk_encounter_pokemon(self, lureinfo): if lureinfo['active_pokemon_id'] in IGNORE_POKEMON: return False try: encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] if resp['result'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id, fort_id, resp) capture_status = catch_attempt['status'] if capture_status == 1: pokemon = resp['pokemon_data'] self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info(resp) self.log.info(colored("Caught pokemon:", "green") + " %s CP: %s, IV: %s", self.pokemon_data[str(pokemon['pokemon_id'])]['name'], str(pokemon['cp']), pokemonIVPercentage(pokemon)) sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info(colored("Failed to catch Pokemon:", "red") + " %s", self.pokemon_data[str(resp['pokemon_data']['pokemon_id'])]['name']) return False sleep(2) # If you want to make it faster, delete this line... would not recommend though except Exception as e: self.log.error("Error in disk encounter %s", e) return False def encounter_pokemon(self,pokemon): encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] position = self._posf encounter = self.encounter(encounter_id=encounter_id,spawn_point_id=spawn_point_id,player_latitude=position[0],player_longitude=position[1]).call()['responses']['ENCOUNTER'] pokemon = encounter['wild_pokemon']['pokemon_data'] if encounter['status'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: catch_attempt = self.attempt_catch(encounter_id, spawn_point_id, encounter) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info(colored("Caught pokemon:", "green") + " %s CP: %s, IV: %s", self.pokemon_data[str(pokemon['pokemon_id'])]['name'], str(pokemon['cp']), pokemonIVPercentage(pokemon)) sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info(colored("Failed to Catch Pokemon:", "red") + " %s", self.pokemon_data[str(pokemon['pokemon_id'])]['name']) return False sleep(2) # If you want to make it faster, delete this line... would not recommend though return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) if cached and os.path.isfile("accounts/%s.json" % username): with open("accounts/%s.json" % username, 'r') as file_contents: jsonstr = file_contents.read() account_json = json.loads(jsonstr) self.log.info('restoring location to %f, %f', account_json['lat'], account_json['lng']) self.set_position(account_json['lat'], account_json['lng'], account_json['alt']) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): self.heartbeat() while True: self.heartbeat() sleep(1) # If you want to make it faster, delete this line... would not recommend though self.spin_near_fort() while self.catch_near_pokemon(): sleep(4) # If you want to make it faster, delete this line... would not recommend though pass @staticmethod def flatmap(f, items): return chain.from_iterable(imap(f, items))
def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) if cached and os.path.isfile("accounts/%s.json" % username): with open("accounts/%s.json" % username, 'r') as file_contents: jsonstr = file_contents.read() account_json = json.loads(jsonstr) self.log.info('restoring location to %f, %f', account_json['lat'], account_json['lng']) self.set_position(account_json['lat'], account_json['lng'], account_json['alt']) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names, start_pos): self.log = logging.getLogger(__name__) self._start_pos = start_pos self._walk_count = 1 self._auth_provider = None self._api_endpoint = None self.config = config self.set_position(*start_pos) self._pokeball_type = 1 self.MIN_KEEP_IV = config.get("MIN_KEEP_IV", 0) self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) self.RELEASE_DUPLICATES = config.get("RELEASE_DUPLICATE", 0) self.DUPLICATE_CP_FORGIVENESS = config.get("DUPLICATE_CP_FORGIVENESS", 0) self.MAX_BALL_TYPE = config.get("MAX_BALL_TYPE", 0) self._req_method_list = [] self._heartbeat_number = 5 self.pokemon_names = pokemon_names self.pokeballs = [0, 0, 0, 0] # pokeball counts. set to 0 to force atleast one fort check before trying to capture pokemon self.min_item_counts = dict( ((getattr(Inventory, key), value) for key, value in config.get('MIN_ITEM_COUNTS', {}).iteritems()) ) def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.debug("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def heartbeat(self): self.get_player() if self._heartbeat_number % 10 == 0: # every 10 heartbeats do a inventory check self.check_awarded_badges() self.get_inventory() res = self.call() if res.get("direction", -1) == 102: self.log.error("There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format(json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) if os.path.isfile("accounts/%s.json" % self.config['username']): with open("accounts/%s.json" % self.config['username'], "r") as f: file = f.read() json_file = json.loads(file) inventory_items = json_file.get('GET_INVENTORY', {}).get('inventory_delta', {}).get('inventory_items', []) inventory_items_dict_list = map(lambda x: x.get('inventory_item_data', {}), inventory_items) player_stats = filter(lambda x: 'player_stats' in x, inventory_items_dict_list)[0].get('player_stats', {}) else: player_stats = {} currencies = player_data.get('currencies', []) currency_data = ",".join(map(lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) self.log.info("Username: %s, Lvl: %s, XP: %s/%s, Currencies: %s", player_data.get('username', 'NA'), player_stats.get('level', 'NA'), player_stats.get('experience', 'NA'), player_stats.get('next_level_xp', 'NA'), currency_data) # display stats if 'GET_INVENTORY' in res['responses']: with open("accounts/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) self.log.info("List of Pokemon:\n" + get_inventory_data(res, self.pokemon_names) + "\nTotal Pokemon count: " + str(get_pokemon_num(res)) + "\nEgg Hatching status: " + get_incubators_stat(res)) self.log.debug(self.cleanup_inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'])) self._heartbeat_number += 1 return res def walk_to(self, loc): self._walk_count += 1 steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", "")) for step in steps: for i, next_point in enumerate(get_increments(self._posf, step, self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() self.log.info("Sleeping before next heartbeat") sleep(2) # If you want to make it faster, delete this line... would not recommend though # make sure we have atleast 1 ball if sum(self.pokeballs) > 0: while self.catch_near_pokemon(): sleep(1) # If you want to make it faster, delete this line... would not recommend though # this is in charge of spinning a pokestop def spin_near_fort(self): map_cells = self.nearby_map_objects().get('responses', {}).get('GET_MAP_OBJECTS', {}).get('map_cells', {}) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) if self._start_pos and self._walk_count % self.config.get("RETURN_START_INTERVAL") == 0: destinations = filtered_forts(self._start_pos, forts) else: destinations = filtered_forts(self._posf, forts) if destinations: destination_num = random.randint(0, min(5, len(destinations) - 1)) fort = destinations[destination_num] self.log.info("Walking to fort at %s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) position = self._posf # FIXME ? res = self.fort_search(fort_id=fort['id'], fort_latitude=fort['latitude'], fort_longitude=fort['longitude'], player_latitude=position[0], player_longitude=position[1]).call()['responses']['FORT_SEARCH'] self.log.debug("Fort spinned: %s", res) if 'lure_info' in fort: encounter_id = fort['lure_info']['encounter_id'] fort_id = fort['lure_info']['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] self.log.debug('Encounter response is: %s', resp) if self.pokeballs[1] > 9 and self.pokeballs[2] > 4 and self.pokeballs[3] > 4: self.disk_encounter_pokemon(fort['lure_info']) return True else: self.log.error("No fort to walk to!") return False # this will catch any nearby pokemon def catch_near_pokemon(self): map_cells = self.nearby_map_objects().get('responses', {}).get('GET_MAP_OBJECTS', {}).get('map_cells', {}) pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [(pokemon, distance_in_meters(origin, (pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons] self.log.debug("Nearby pokemon: : %s", pokemon_distances) for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) self.log.info("Catching Pokemon: %s", self.pokemon_names[str(target[0]['pokemon_id'])]) return self.encounter_pokemon(target[0]) if sum(self.pokeballs) == 0: self.spin_near_fort() return False def nearby_map_objects(self): position = self.get_position() neighbors = get_neighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0] * len(neighbors), cell_id=neighbors).call() def attempt_catch(self, encounter_id, spawn_point_guid, ball_type): r = self.catch_pokemon( normalized_reticle_size=1.950, pokeball=ball_type, spin_modifier=0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_guid=spawn_point_guid, ).call()['responses']['CATCH_POKEMON'] self.log.info("Throwing pokeball type: %s", POKEBALLS[ball_type - 1]) # list the pokeball that was thrown if "status" in r: self.log.debug("Status: %d", r['status']) return r def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] all_actual_items = [xiq['inventory_item_data']["item"] for xiq in inventory_items if "item" in xiq['inventory_item_data']] all_actual_item_str = "List of items:\n" all_actual_item_count = 0 all_actual_items = sorted([x for x in all_actual_items if "count" in x], key=lambda x: x["item_id"]) for xiq in all_actual_items: if 1 <= xiq["item_id"] <= 4: # save counts of pokeballs self.pokeballs[xiq["item_id"]] = xiq["count"] true_item_name = INVENTORY_DICT[xiq["item_id"]] all_actual_item_str += "Item_ID " + str(xiq["item_id"]) + "\titem count " + str(xiq["count"]) + "\t(" + true_item_name + ")\n" all_actual_item_count += xiq["count"] all_actual_item_str += "Total item count: " + str(all_actual_item_count) self.log.info(all_actual_item_str) caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = inventory_item['inventory_item_data']['pokemon_data'] if 'cp' in pokemon and "favorite" not in pokemon: caught_pokemon[pokemon["pokemon_id"]].append(pokemon) elif "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in self.min_item_counts and "count" in item and item['count'] > self.min_item_counts[item['item_id']]: recycle_count = item['count'] - self.min_item_counts[item['item_id']] self.log.info("Recycling {0}, item count {1}".format(INVENTORY_DICT[item['item_id']], recycle_count)) self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count) for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: # if you have more then same amount of pokemon do this pokemons = sorted(pokemons, lambda x, y: cmp(x['cp'], y['cp']), reverse=True) for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if 'cp' in pokemon and pokemon_iv_percentage(pokemon) > self.MIN_KEEP_IV and pokemon["cp"] > self.KEEP_CP_OVER: # Keep only if the pokemon is over the IV and CP set up if pokemon['pokemon_id'] in CANDY_NEEDED_TO_EVOLVE: for inventory_item in inventory_items: if "pokemon_family" in inventory_item['inventory_item_data'] and inventory_item['inventory_item_data']['pokemon_family']['family_id'] == pokemon['pokemon_id'] and inventory_item['inventory_item_data']['pokemon_family']['candy'] > CANDY_NEEDED_TO_EVOLVE[pokemon['pokemon_id']]: self.log.info("Evolving pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self.evolve_pokemon(pokemon_id=pokemon['id']) else: if pokemon['pokemon_id'] in CANDY_NEEDED_TO_EVOLVE: for inventory_item in inventory_items: if "pokemon_family" in inventory_item['inventory_item_data'] and inventory_item['inventory_item_data']['pokemon_family']['family_id'] == pokemon['pokemon_id'] and inventory_item['inventory_item_data']['pokemon_family']['candy'] > CANDY_NEEDED_TO_EVOLVE[pokemon['pokemon_id']]: self.log.info("Evolving pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self.evolve_pokemon(pokemon_id=pokemon['id']) self.log.debug("Releasing pokemon: %s", pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(pokemon['pokemon_id'])], pokemon_iv_percentage(pokemon)) self.release_pokemon(pokemon_id=pokemon["id"]) if self.RELEASE_DUPLICATES: for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, lambda x, y: cmp(self.pokemon_names[str(x['pokemon_id'])], self.pokemon_names[str(y['pokemon_id'])])) last_pokemon = pokemons[0] for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if self.pokemon_names[str(pokemon['pokemon_id'])] == self.pokemon_names[str(last_pokemon['pokemon_id'])]: # Compare two pokemon if the larger IV pokemon has less then DUPLICATE_CP_FORGIVENESS times CP keep it if pokemon_iv_percentage(pokemon) > pokemon_iv_percentage(pokemon): if pokemon['cp'] * self.DUPLICATE_CP_FORGIVENESS < last_pokemon['cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", last_pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(last_pokemon['pokemon_id'])], pokemon_iv_percentage(last_pokemon)) self.release_pokemon(pokemon_id=last_pokemon["id"]) last_pokemon = pokemon else: if last_pokemon['cp'] * self.DUPLICATE_CP_FORGIVENESS > pokemon['cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", pokemon) self.log.info("Releasing pokemon: %s IV: %s", self.pokemon_names[str(pokemon['pokemon_id'])], pokemon_iv_percentage(pokemon)) self.release_pokemon(pokemon_id=pokemon["id"]) else: last_pokemon = pokemon return self.call() def disk_encounter_pokemon(self, lureinfo): try: encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] if resp['result'] == 1: capture_status = -1 self._pokeball_type = 1 while capture_status != 0 and capture_status != 3: for balls in range(len(self.pokeballs)): self._pokeball_type = balls if self.pokeballs[balls] > 0: catch_attempt = self.attempt_catch(encounter_id, fort_id, self._pokeball_type) self.pokeballs[self._pokeball_type] -= 1 capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info("Caught Pokemon: %s", self.pokemon_names[str(resp['pokemon_data']['pokemon_id'])]) self._pokeball_type = 1 sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: self.log.info("Pokemon %s is too wild", self.pokemon_names[str(resp['pokemon_data']['pokemon_id'])]) if self._pokeball_type < self.MAX_BALL_TYPE: self._pokeball_type += 1 elif capture_status == 3: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info("Failed to Catch Pokemon: %s", self.pokemon_names[str(resp['pokemon_data']['pokemon_id'])]) self._pokeball_type = 1 sleep(2) # If you want to make it faster, delete this line... would not recommend though return False except Exception as e: self.log.error("Error in disk encounter %s", e) self._pokeball_type = 1 return False def encounter_pokemon(self, pokemon): encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] position = self._posf encounter = self.encounter( encounter_id=encounter_id, spawn_point_id=spawn_point_id, player_latitude=position[0], player_longitude=position[1] ).call()['responses']['ENCOUNTER'] # this cade catches pokemon self.log.debug("Started Encounter: %s", encounter) if encounter['status'] == 1: capture_status = -1 self._pokeball_type = 1 # start with a pokeball while capture_status != 0 and capture_status != 3: for balls in range(len(self.pokeballs)): # try with each ball type starting with weakest self._pokeball_type = balls if self.pokeballs[balls] > 0: # if you have less then 1 ball do not attempt to catch em all catch_attempt = self.attempt_catch(encounter_id, spawn_point_id, self._pokeball_type) # actual catching code self.pokeballs[self._pokeball_type] -= 1 # lowers the thrown ball code capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) # you did it self.log.info("Caught Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self._pokeball_type = 1 sleep(2) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: self.log.info("Pokemon %s is too wild", self.pokemon_names[str(pokemon['pokemon_id'])]) if self._pokeball_type < self.MAX_BALL_TYPE: self._pokeball_type += 1 # try with a stronger ball elif capture_status == 3: self.log.debug("Failed Catch: : %s", catch_attempt) # potential soft ban or just a run away self.log.info("Failed to Catch Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self._pokeball_type = 1 sleep(2) # If you want to make it faster, delete this line... would not recommend though return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") # not sure what this is but dont change it response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): self.heartbeat() while True: self.heartbeat() sleep(1) # If you want to make it faster, delete this line... would not recommend though if sum(self.pokeballs) > 0: # if you do not have any balls skip pokemon catching while self.catch_near_pokemon(): sleep(4) # If you want to make it faster, delete this line... would not recommend though else: self.log.info("Less than 1 Poke Balls: Entering pokestops only") self.spin_near_fort() # check local pokestop @staticmethod def flatmap(f, items): return chain.from_iterable(imap(f, items))
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self._position_lat = 0 self._position_lng = 0 self._position_alt = 0 self._req_method_list = [] def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.info('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.info('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.info('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.info("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.info("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def login(self, provider, username, password): if not isinstance(username, str) or not isinstance(password, str): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self.config = config self._position_lat = 0 # int cooords self._position_lng = 0 self._position_alt = 0 self._posf = (0, 0, 0) # this is floats self._origPosF = (0, 0, 0) # this is original position in floats self._req_method_list = [] self._heartbeat_number = 5 self._firstRun = True self._last_egg_use_time = 0 self.pokemon_caught = 0 self.player = Player({}) self.player_stats = PlayerStats({}) self.inventory = Player_Inventory([]) self.pokemon_names = pokemon_names self.start_time = time() self.exp_start = None self.exp_current = None self.MIN_ITEMS = {} for k, v in config.get("MIN_ITEMS", {}).items(): self.MIN_ITEMS[getattr(Inventory, k)] = v self.POKEMON_EVOLUTION = {} self.POKEMON_EVOLUTION_FAMILY = {} for k, v in config.get("POKEMON_EVOLUTION", {}).items(): self.POKEMON_EVOLUTION[getattr(Enums_pb2, k)] = v self.POKEMON_EVOLUTION_FAMILY[getattr(Enums_pb2, k)] = getattr(Enums_pb2, "FAMILY_" + k) self.KEEP_IV_OVER = config.get("KEEP_IV_OVER", 0) # release anything under this self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) # release anything under this self.MIN_SIMILAR_POKEMON = config.get("MIN_SIMILAR_POKEMON", 1) # Keep atleast one of everything. self.STAY_WITHIN_PROXIMITY = config.get("STAY_WITHIN_PROXIMITY", 9999999) # Stay within proximity self.LIST_POKEMON_BEFORE_CLEANUP = config.get("LIST_POKEMON_BEFORE_CLEANUP", True) # list pokemon in console self.LIST_INVENTORY_BEFORE_CLEANUP = config.get("LIST_INVENTORY_BEFORE_CLEANUP", True) # list inventory in console self.EGG_INCUBATION_ENABLED = config.get("EGG_INCUBATION", {}).get("ENABLE", True) self.USE_DISPOSABLE_INCUBATORS = config.get("EGG_INCUBATION", {}).get("USE_DISPOSABLE_INCUBATORS", False) self.INCUBATE_BIG_EGGS_FIRST = config.get("EGG_INCUBATION", {}).get("BIG_EGGS_FIRST", True) self.visited_forts = ExpiringDict(max_len=120, max_age_seconds=config.get("SKIP_VISITED_FORT_DURATION", 600)) self.experimental = config.get("EXPERIMENTAL", False) self.spin_all_forts = config.get("SPIN_ALL_FORTS", False) self.keep_pokemon_ids = map(lambda x: getattr(Enums_pb2, x), config.get("KEEP_POKEMON_NAMES", [])) self.throw_pokemon_ids = map(lambda x: getattr(Enums_pb2, x), config.get("THROW_POKEMON_NAMES", [])) self.max_catch_attempts = config.get("MAX_CATCH_ATTEMPTS", 10) self.game_master = parse_game_master() self.should_catch_pokemon = config.get("CATCH_POKEMON", True) self.RELEASE_DUPLICATES = config.get("RELEASE_DUPLICATES", False) self.RELEASE_DUPLICATES_MAX_LV = config.get("RELEASE_DUPLICATES_MAX_LV", 0) # only release duplicates up to this lvl self.RELEASE_DUPLICATES_SCALER = config.get("RELEAES_DUPLICATES_SCALER", 1.0) # when comparing two pokemon's lvl, multiply larger by this self.DEFINE_POKEMON_LV = config.get("DEFINE_POKEMON_LV", "CP") # define a pokemon's lvl, options are CP, IV, CP*IV, CP+IV def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) if self._firstRun: self._firstRun = False self._origPosF = self._posf self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.debug("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def hourly_exp(self, exp): if self.exp_start is None: self.exp_start = exp self.exp_current = exp run_time = time() - self.start_time run_time_hours = float(run_time/3600.00) exp_earned = float(self.exp_current - self.exp_start) exp_hour = float(exp_earned/run_time_hours) self.log.info("=== Exp/Hour: %s ===", round(exp_hour,2)) return exp_hour def update_player_inventory(self): self.get_inventory() res = self.call() if 'GET_INVENTORY' in res['responses']: self.inventory = Player_Inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']) return res def heartbeat(self): # making a standard call to update position, etc self.get_player() if self._heartbeat_number % 10 == 0: self.check_awarded_badges() self.get_inventory() # self.download_settings(hash="4a2e9bc330dae60e7b74fc85b98868ab4700802e") res = self.call() if not res or res.get("direction", -1) == 102: self.log.error("There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format(json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: self.player = Player(res['responses'].get('GET_PLAYER', {}).get('player_data', {})) self.log.info("Player Info: %s, Pokemon Caught in this run: %s", self.player, self.pokemon_caught) if 'GET_INVENTORY' in res['responses']: with open("data_dumps/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) self.inventory = Player_Inventory(res['responses']['GET_INVENTORY']['inventory_delta']['inventory_items']) for inventory_item in self.inventory.inventory_items: if "player_stats" in inventory_item['inventory_item_data']: self.player_stats = PlayerStats(inventory_item['inventory_item_data']['player_stats']) self.log.info("Player Stats: %s", self.player_stats) self.hourly_exp(self.player_stats.experience) if self.LIST_INVENTORY_BEFORE_CLEANUP: self.log.info("Player Items Before Cleanup: %s", self.inventory) self.log.debug(self.cleanup_inventory(self.inventory.inventory_items)) self.log.info("Player Inventory after cleanup: %s", self.inventory) if self.LIST_POKEMON_BEFORE_CLEANUP: self.log.info(get_inventory_data(res, self.pokemon_names)) self.incubate_eggs() self.attempt_evolve(self.inventory.inventory_items) self.cleanup_pokemon(self.inventory.inventory_items) # Auto-use lucky-egg if applicable self.use_lucky_egg() self._heartbeat_number += 1 return res def use_lucky_egg(self): if self.config.get("AUTO_USE_LUCKY_EGG", False) and self.inventory.has_lucky_egg() and time() - self._last_egg_use_time > 30*60: self.use_item_xp_boost(item_id=Inventory.ITEM_LUCKY_EGG) response = self.call() result = response.get('responses', {}).get('USE_ITEM_XP_BOOST', {}).get('result', -1) if result == 1: self.log.info("Ate a lucky egg! Yummy! :)") self.inventory.take_lucky_egg() self._last_egg_use_time = time() return True elif result == 3: self.log.info("Lucky egg already active") return False else: self.log.info("Lucky Egg couldn't be used, status code %s", result) return False else: return False def walk_to(self, loc, waypoints=[], directly=False): # location in floats of course... steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", ""), self.experimental and self.spin_all_forts, waypoints) catch_attempt = 0 base_travel_link = "https://www.google.com/maps/dir/%s,%s/" % (self._posf[0], self._posf[1]) step_size = self.config.get("STEP_SIZE", 200) total_distance_traveled = 0 total_distance = distance_in_meters(self._posf, loc) new_loc = (loc[0], loc[1], 0) for step in steps: for i, next_point in enumerate(get_increments(self._posf, step, step_size)): # we are less than a step away, lets just go there! travel_remaining = total_distance - total_distance_traveled distance_to_point = distance_in_meters(self._posf, next_point) if travel_remaining < step_size or distance_to_point + total_distance_traveled > total_distance: next_point = new_loc distance_to_point = distance_in_meters(self._posf, next_point) total_distance_traveled += distance_to_point self.log.info('=================================') self.log.info( "On my way to the next fort! :) Traveled %.2f meters of %.2f ", total_distance_traveled, total_distance, ) travel_link = '%s%s,%s' % (base_travel_link, next_point[0], next_point[1]) self.log.info("Travel Link: %s", travel_link) self.set_position(*next_point) self.heartbeat() if directly is False: if self.experimental and self.spin_all_forts: self.spin_nearest_fort() sleep(1) while self.catch_near_pokemon() and catch_attempt <= self.max_catch_attempts: sleep(1) catch_attempt += 1 catch_attempt = 0 # Don't continue with the steps if we've reached our location if next_point == new_loc: self.log.info('=================================') return self.log.info('=================================') def walk_back_to_origin(self): self.walk_to(self._origPosF) def spin_nearest_fort(self): map_cells = self.nearby_map_objects()['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts) if destinations: nearest_fort = destinations[0][0] nearest_fort_dis = destinations[0][1] self.log.info('Nearest fort distance is %s', nearest_fort_dis) # Fort is close enough to change our route and walk to if nearest_fort_dis > 40.00 and nearest_fort_dis <= 100: lat = nearest_fort['latitude'] long = nearest_fort['longitude'] self.walk_to_fort(destinations[0], directly=True) self.fort_search_pgoapi(nearest_fort, player_postion=self.get_position(), fort_distance=nearest_fort_dis) if nearest_fort_dis <= 40.00: self.fort_search_pgoapi(nearest_fort, player_postion=self.get_position(), fort_distance=nearest_fort_dis) if 'lure_info' in nearest_fort and self.should_catch_pokemon: self.disk_encounter_pokemon(nearest_fort['lure_info']) else: self.log.info('No spinnable forts within proximity. Or server returned no map objects.') def fort_search_pgoapi(self, fort, player_postion, fort_distance): res = self.fort_search(fort_id=fort['id'], fort_latitude=fort['latitude'], fort_longitude=fort['longitude'], player_latitude=player_postion[0], player_longitude=player_postion[1]).call()['responses']['FORT_SEARCH'] result = res.pop('result', -1) if result == 1 and res: self.log.info("Visiting fort... (http://maps.google.com/maps?q=%s,%s)", fort['latitude'], fort['longitude']) if "items_awarded" in res: items = defaultdict(int) for item in res['items_awarded']: items[item['item_id']] += item['item_count'] reward = 'XP +' + str(res['experience_awarded']) for item_id, amount in items.iteritems(): reward += ', ' + str(amount) + 'x ' + get_item_name(item_id) self.log.info("Fort spun, yielding: %s", reward) else: self.log.info("Fort spun, but did not yield any rewards. Possible soft ban?") self.visited_forts[fort['id']] = fort elif result == 4: self.log.debug("For spinned but Your inventory is full : %s", res) self.log.info("For spinned but Your inventory is full.") self.visited_forts[fort['id']] = fort elif result == 2: self.log.debug("Could not spin fort - fort not in range %s", res) self.log.info("Could not spin fort http://maps.google.com/maps?q=%s,%s, Not in Range %s", fort['latitude'], fort['longitude'], fort_distance) else: self.log.debug("Could not spin fort %s", res) self.log.info("Could not spin fort http://maps.google.com/maps?q=%s,%s, Error id: %s", fort['latitude'], fort['longitude'], result) return False return True def spin_all_forts_visible(self): res = self.nearby_map_objects() map_cells = res['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts, self.experimental) if not destinations: self.log.info("No fort to walk to! %s", res) self.log.info('No more spinnable forts within proximity. Or server error') self.walk_back_to_origin() return False if len(destinations) >= 20: destinations = destinations[:20] furthest_fort = destinations[0][0] self.log.info("Walking to fort at http://maps.google.com/maps?q=%s,%s", furthest_fort['latitude'], furthest_fort['longitude']) self.walk_to((furthest_fort['latitude'], furthest_fort['longitude']), map(lambda x: "via:%f,%f" % (x[0]['latitude'], x[0]['longitude']), destinations[1:])) return True def return_to_start(self): self.set_position(*self._origPosF) def walk_to_fort(self, fort_data, directly=False): fort = fort_data[0] self.log.info("Walking to fort at http://maps.google.com/maps?q=%s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude']), directly=directly) self.fort_search_pgoapi(fort, self.get_position(), fort_data[1]) if 'lure_info' in fort: self.disk_encounter_pokemon(fort['lure_info']) def spin_near_fort(self): res = self.nearby_map_objects() map_cells = res['responses'].get('GET_MAP_OBJECTS', {}).get('map_cells', []) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._origPosF, self._posf, forts, self.STAY_WITHIN_PROXIMITY, self.visited_forts, self.experimental) if not destinations: self.log.debug("No fort to walk to! %s", res) self.log.info('No more spinnable forts within proximity. Returning back to origin') self.walk_back_to_origin() return False for fort_data in destinations: self.walk_to_fort(fort_data) return True def catch_near_pokemon(self): if self.should_catch_pokemon is False: return False map_cells = self.nearby_map_objects()['responses']['GET_MAP_OBJECTS']['map_cells'] pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [(pokemon, distance_in_meters(origin, (pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons] if pokemons: self.log.debug("Nearby pokemon: : %s", pokemon_distances) self.log.info("Nearby Pokemon: %s", ", ".join(map(lambda x: self.pokemon_names[str(x['pokemon_id'])], pokemons))) else: self.log.info("No nearby pokemon") catches_successful = False for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) catches_successful &= self.encounter_pokemon(target[0]) sleep(random.randrange(4, 8)) return catches_successful def nearby_map_objects(self): position = self.get_position() neighbors = getNeighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0] * len(neighbors), cell_id=neighbors).call() def attempt_catch(self, encounter_id, spawn_point_id, capture_probability=None): catch_status = -1 catch_attempts = 1 ret = {} if not capture_probability: capture_probability = {} # Max 4 attempts to catch pokemon while catch_status != 1 and self.inventory.can_attempt_catch() and catch_attempts < 11: pokeball = self.inventory.take_next_ball(capture_probability) self.log.info("Attempting catch with ball type {0} at {1:.2f} % chance. Try Number: {2}".format(pokeball, capture_probability.get(pokeball, 0.0) * 100, catch_attempts)) r = self.catch_pokemon( normalized_reticle_size=1.950, pokeball=pokeball, spin_modifier=0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_id=spawn_point_id, ).call()['responses']['CATCH_POKEMON'] catch_attempts += 1 if "status" in r: catch_status = r['status'] # fleed or error if catch_status == 3 or catch_status == 0: break ret = r # Sleep between catch attempts sleep(3) # Sleep after the catch (the pokemon animation time) sleep(4) return ret def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] item_count = 0 for inventory_item in inventory_items: if "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in self.MIN_ITEMS and "count" in item and item['count'] > self.MIN_ITEMS[ item['item_id']]: recycle_count = item['count'] - self.MIN_ITEMS[item['item_id']] item_count += item['count'] - recycle_count self.log.info("Recycling Item_ID {0}, item count {1}".format(item['item_id'], recycle_count)) res = self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count).call()['responses'][ 'RECYCLE_INVENTORY_ITEM'] response_code = res['result'] if response_code == 1: self.log.info("Recycled Item %s, New Count: %s", item['item_id'], res.get('new_count', 0)) else: self.log.info("Failed to recycle Item %s, Code: %s", item['item_id'], response_code) sleep(2) elif "count" in item: item_count += item['count'] if item_count > 0: self.log.info('Intentory has %s/%s items', item_count, self.player.max_item_storage) return self.update_player_inventory() def get_caught_pokemons(self, inventory_items): caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = Pokemon(inventory_item['inventory_item_data']['pokemon_data'], self.pokemon_names) pokemon.pokemon_additional_data = self.game_master.get(pokemon.pokemon_id, PokemonData()) if not pokemon.is_egg: caught_pokemon[pokemon.pokemon_id].append(pokemon) return caught_pokemon def do_release_pokemon(self, pokemon): self.log.info("Releasing pokemon: %s", pokemon) self.release_pokemon(pokemon_id=pokemon.id) release_res = self.call()['responses']['RELEASE_POKEMON'] status = release_res.get('result', -1) if status == 1: self.log.info("Successfully Released Pokemon %s", pokemon) else: self.log.debug("Failed to release pokemon %s, %s", pokemon, release_res) self.log.info("Failed to release Pokemon %s", pokemon) sleep(3) def cleanup_pokemon(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] caught_pokemon = self.get_caught_pokemons(inventory_items) for pokemons in caught_pokemon.values(): if len(pokemons) > self.MIN_SIMILAR_POKEMON: # highest lvl pokemon first sorted_pokemons = sorted(pokemons, key=self.pokemon_lvl, reverse=True) for pokemon in sorted_pokemons[self.MIN_SIMILAR_POKEMON:]: if self.is_pokemon_eligible_for_transfer(pokemon, sorted_pokemons[0]): self.do_release_pokemon(pokemon) def is_pokemon_eligible_for_transfer(self, pokemon, best_pokemon): # never release favorites and other defined pokemons if pokemon.is_favorite or pokemon.pokemon_id in self.keep_pokemon_ids: return False elif self.RELEASE_DUPLICATES and ( self.pokemon_lvl(best_pokemon) * self.RELEASE_DUPLICATES_SCALER > self.pokemon_lvl( pokemon) and self.pokemon_lvl(pokemon) < self.RELEASE_DUPLICATES_MAX_LV): return True # release defined throwaway pokemons but make sure we have kept at least 1 (dont throw away all of them) elif pokemon.pokemon_id in self.throw_pokemon_ids: return True # keep high-cp pokemons elif pokemon.cp > self.KEEP_CP_OVER or pokemon.iv > self.KEEP_IV_OVER: return False # if we haven't found a reason to keep it, transfer it else: return True def pokemon_lvl(self, pokemon): if self.DEFINE_POKEMON_LV == "CP": return pokemon.cp elif self.DEFINE_POKEMON_LV == "IV": return pokemon.iv elif self.DEFINE_POKEMON_LV == "CP*IV": return pokemon.cp * pokemon.iv elif self.DEFINE_POKEMON_LV == "CP+IV": return pokemon.cp + pokemon.iv def attempt_evolve(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses']['GET_INVENTORY']['inventory_delta'][ 'inventory_items'] caught_pokemon = self.get_caught_pokemons(inventory_items) self.inventory = Player_Inventory(inventory_items) for pokemons in caught_pokemon.values(): if len(pokemons) > self.MIN_SIMILAR_POKEMON: pokemons = sorted(pokemons, key=lambda x: (x.cp, x.iv), reverse=True) for pokemon in pokemons[self.MIN_SIMILAR_POKEMON:]: # If we can't evolve this type of pokemon anymore, don't check others. if not self.attempt_evolve_pokemon(pokemon): break def attempt_evolve_pokemon(self, pokemon): if self.is_pokemon_eligible_for_evolution(pokemon=pokemon): self.log.info("Evolving pokemon: %s", pokemon) evo_res = self.evolve_pokemon(pokemon_id=pokemon.id).call()['responses']['EVOLVE_POKEMON'] status = evo_res.get('result', -1) sleep(3) if status == 1: evolved_pokemon = Pokemon(evo_res.get('evolved_pokemon_data', {}), self.pokemon_names, self.game_master.get(str(pokemon.pokemon_id), PokemonData())) # I don' think we need additional stats for evolved pokemon. Since we do not do anything with it. # evolved_pokemon.pokemon_additional_data = self.game_master.get(pokemon.pokemon_id, PokemonData()) self.log.info("Evolved to %s", evolved_pokemon) self.update_player_inventory() return True else: self.log.debug("Could not evolve Pokemon %s", evo_res) self.log.info("Could not evolve pokemon %s | Status %s", pokemon, status) self.update_player_inventory() return False else: return False def is_pokemon_eligible_for_evolution(self, pokemon): return self.inventory.pokemon_candy.get(self.POKEMON_EVOLUTION_FAMILY.get(pokemon.pokemon_id, None), -1) > self.POKEMON_EVOLUTION.get(pokemon.pokemon_id, None) \ and pokemon.pokemon_id not in self.keep_pokemon_ids \ and not pokemon.is_favorite \ and pokemon.pokemon_id in self.POKEMON_EVOLUTION def disk_encounter_pokemon(self, lureinfo, retry=False): try: self.update_player_inventory() if not self.inventory.can_attempt_catch(): self.log.info("No balls to catch %s, exiting disk encounter", self.inventory) return False encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf self.log.debug("At Fort with lure %s".encode('utf-8', 'ignore'), lureinfo) self.log.info("At Fort with Lure AND Active Pokemon %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['DISK_ENCOUNTER'] pokemon = Pokemon(resp.get('pokemon_data', {}), self.pokemon_names) result = resp.get('result', -1) capture_probability = create_capture_probability(resp.get('capture_probability', {})) self.log.debug("Attempt Encounter: %s", json.dumps(resp, indent=4, sort_keys=True)) if result == 1: return self.do_catch_pokemon(encounter_id, fort_id, capture_probability, pokemon) elif result == 5: self.log.info("Couldn't catch %s Your pokemon bag was full, attempting to clear and re-try", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) self.cleanup_pokemon() if not retry: return self.disk_encounter_pokemon(lureinfo, retry=True) else: self.log.info("Could not start Disk (lure) encounter for pokemon: %s", self.pokemon_names.get(str(lureinfo.get('active_pokemon_id', 0)), "NA")) except Exception as e: self.log.error("Error in disk encounter %s", e) return False def do_catch_pokemon(self, encounter_id, spawn_point_id, capture_probability, pokemon): self.log.info("Catching Pokemon: %s", pokemon) catch_attempt = self.attempt_catch(encounter_id, spawn_point_id, capture_probability) capture_status = catch_attempt.get('status', -1) if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info("Caught Pokemon: %s", pokemon) self.pokemon_caught += 1 return True elif capture_status == 3: self.log.debug("Pokemon fleed : %s", catch_attempt) self.log.info("Pokemon fleed: %s", pokemon) return False elif capture_status == 2: self.log.debug("Pokemon escaped: : %s", catch_attempt) self.log.info("Pokemon escaped: %s", pokemon) return False elif capture_status == 4: self.log.debug("Catch Missed: : %s", catch_attempt) self.log.info("Catch Missed: %s", pokemon) return False else: self.log.debug("Could not catch pokemon: %s", catch_attempt) self.log.info("Could not catch pokemon: %s", pokemon) self.log.info("Could not catch pokemon: %s, status: %s", pokemon, capture_status) return False def encounter_pokemon(self, pokemon_data, retry=False): # take in a MapPokemon from MapCell.catchable_pokemons # Update Inventory to make sure we can catch this mon try: self.update_player_inventory() if not self.inventory.can_attempt_catch(): self.log.info("No balls to catch %s, exiting encounter", self.inventory) return False encounter_id = pokemon_data['encounter_id'] spawn_point_id = pokemon_data['spawn_point_id'] # begin encounter_id position = self.get_position() self.log.info("Trying initiate catching Pokemon: %s", Pokemon(pokemon_data, self.pokemon_names)) encounter = self.encounter(encounter_id=encounter_id, spawn_point_id=spawn_point_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['ENCOUNTER'] self.log.debug("Attempting to Start Encounter: %s", encounter) pokemon = Pokemon(encounter.get('wild_pokemon', {}).get('pokemon_data', {}), self.pokemon_names) result = encounter.get('status', -1) capture_probability = create_capture_probability(encounter.get('capture_probability', {})) self.log.debug("Attempt Encounter Capture Probability: %s", json.dumps(encounter, indent=4, sort_keys=True)) if result == 1: return self.do_catch_pokemon(encounter_id, spawn_point_id, capture_probability, pokemon) elif result == 7: self.log.info("Couldn't catch %s Your pokemon bag was full, attempting to clear and re-try", pokemon) self.cleanup_pokemon() if not retry: return self.encounter_pokemon(pokemon, retry=True) else: self.log.info("Could not start encounter for pokemon: %s", pokemon) return False except Exception as e: self.log.error("Error in pokemon encounter %s", e) return False def incubate_eggs(self): if not self.EGG_INCUBATION_ENABLED: return if self.player_stats.km_walked > 0: for incubator in self.inventory.incubators_busy: incubator_egg_distance = incubator['target_km_walked'] - incubator['start_km_walked'] incubator_distance_done = self.player_stats.km_walked - incubator['start_km_walked'] if incubator_distance_done > incubator_egg_distance: self.attempt_finish_incubation() break for incubator in self.inventory.incubators_busy: incubator_egg_distance = incubator['target_km_walked'] - incubator['start_km_walked'] incubator_distance_done = self.player_stats.km_walked - incubator['start_km_walked'] self.log.info('Incubating %skm egg, %skm done', incubator_egg_distance, round(incubator_distance_done, 2)) for incubator in self.inventory.incubators_available: if incubator['item_id'] == 901: # unlimited use pass elif self.USE_DISPOSABLE_INCUBATORS and incubator['item_id'] == 902: # limited use pass else: continue eggs_available = self.inventory.eggs_available eggs_available = sorted(eggs_available, key=lambda egg: egg['creation_time_ms'], reverse=False) # oldest first eggs_available = sorted(eggs_available, key=lambda egg: egg['egg_km_walked_target'], reverse=self.INCUBATE_BIG_EGGS_FIRST) # now sort as defined if not len(eggs_available) > 0 or not self.attempt_start_incubation(eggs_available[0], incubator): break def attempt_start_incubation(self, egg, incubator): self.log.info("Start incubating %skm egg", egg['egg_km_walked_target']) incubate_res = self.use_item_egg_incubator(item_id=incubator['id'], pokemon_id=egg['id']).call()['responses']['USE_ITEM_EGG_INCUBATOR'] status = incubate_res.get('result', -1) sleep(3) if status == 1: self.log.info("Incubation started with %skm egg !", egg['egg_km_walked_target']) self.update_player_inventory() return True else: self.log.debug("Could not start incubating %s", incubate_res) self.log.info("Could not start incubating %s egg | Status %s", egg['egg_km_walked_target'], status) self.update_player_inventory() return False def attempt_finish_incubation(self): self.log.info("Checking for hatched eggs") hatch_res = self.get_hatched_eggs().call()['responses']['GET_HATCHED_EGGS'] status = hatch_res.get('success', -1) sleep(3) if status == 1: self.update_player_inventory() i = 0 for pokemon_id in hatch_res['pokemon_id']: pokemon = get_pokemon_by_long_id(pokemon_id, self.inventory.inventory_items, self.pokemon_names) self.log.info("Egg Hatched! XP +%s, Candy +%s, Stardust +%s, %s", hatch_res['experience_awarded'][i], hatch_res['candy_awarded'][i], hatch_res['stardust_awarded'][i], pokemon) i += 1 return True else: self.log.debug("Could not get hatched eggs %s", hatch_res) self.log.info("Could not get hatched eggs Status %s", status) self.update_player_inventory() return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance(password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): catch_attempt = 0 self.heartbeat() while True: self.heartbeat() sleep(1) if self.experimental and self.spin_all_forts: self.spin_all_forts_visible() else: self.spin_near_fort() # if catching fails 10 times, maybe you are sofbanned. while self.catch_near_pokemon() and catch_attempt <= self.max_catch_attempts: sleep(4) catch_attempt += 1 pass if catch_attempt > self.max_catch_attempts: self.log.warn("Your account may be softbaned Or no Pokeballs. Failed to catch pokemon %s times", catch_attempt) catch_attempt = 0 @staticmethod def flatmap(f, items): return list(chain.from_iterable(imap(f, items)))
class PGoApi: def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None): self.set_logger() self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None if provider is not None and ( (username is not None and password is not None) or (oauth2_refresh_token is not None)): self.set_authentication(provider, oauth2_refresh_token, username, password) self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") self._position_lat = position_lat self._position_lng = position_lng self._position_alt = position_alt self._signature_lib = None def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None): if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username is not None and password is not None: self._auth_provider.user_login(username, password) else: raise AuthException( "Invalid Credential Input - Please provide username/password or an oauth2 refresh token" ) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def get_api_endpoint(self): return self._api_endpoint def set_api_endpoint(self, api_url): if api_url.startswith("https"): self._api_endpoint = api_url else: self._api_endpoint = parse_api_endpoint(api_url) def get_auth_provider(self): return self._auth_provider def create_request(self): request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt) return request def activate_signature(self, lib_path): self._signature_lib = lib_path def get_signature_lib(self): return self._signature_lib def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def app_simulation_login(self): self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client request = self.create_request() request.get_player() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings( hash="54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5") response = request.call() self.log.info('Finished RPC login sequence (app simulation)') return response """ The login function is not needed anymore but still in the code for backward compatibility" """ def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): if lat is not None and lng is not None and alt is not None: self._position_lat = lat self._position_lng = lng self._position_alt = alt try: self.set_authentication(provider, username=username, password=password) except AuthException as e: self.log.error('Login process failed: %s', e) return False if app_simulation: response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self.config = config self._position_lat = 0 self._position_lng = 0 self._position_alt = 0 self._posf = (0, 0, 0) self.MIN_KEEP_IV = config.get("MIN_KEEP_IV", 0) self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) self._req_method_list = [] self._heartbeat_number = 5 self.pokemon_names = pokemon_names self.AUTO_EVOLVE_POKEMON_IDS = config.get("AUTO_EVOLVE_POKEMON_IDS", []) self.KEEP_BEST_POKEMON_ONLY = config.get("KEEP_BEST_POKEMON_ONLY", False) self.KEEP_BEST_POKEMON_MIN_CP = config.get("KEEP_BEST_POKEMON_MIN_CP", 100) self.KEEP_BEST_POKEMON_MIN_IV = config.get("KEEP_BEST_POKEMON_MIN_IV", 20) def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.debug( "Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def heartbeat(self): self.get_player() if self._heartbeat_number % 10 == 0: self.check_awarded_badges() self.get_inventory() res = self.call() if res.get("direction", -1) == 102: self.log.error( "There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format( json.dumps(res, indent=2))) if self._heartbeat_number % 20 == 0: if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) if os.path.isfile("accounts/%s.json" % self.config['username']): with open("accounts/%s.json" % self.config['username'], "r") as f: file = f.read() json_file = json.loads(file) inventory_items = json_file.get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', []) inventory_items_dict_list = map( lambda x: x.get('inventory_item_data', {}), inventory_items) player_stats = filter(lambda x: 'player_stats' in x, inventory_items_dict_list)[0].get( 'player_stats', {}) else: player_stats = {} currencies = player_data.get('currencies', []) currency_data = ",".join( map( lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) self.log.info( "Username: %s, Lvl: %s, XP: %s/%s, Currencies: %s", player_data.get('username', 'NA'), player_stats.get('level', 'NA'), player_stats.get('experience', 'NA'), player_stats.get('next_level_xp', 'NA'), currency_data) if 'GET_INVENTORY' in res['responses']: with open("accounts/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) # self.log.info(get_inventory_data(res, self.pokemon_names)) self.log.debug( self.cleanup_inventory( res['responses']['GET_INVENTORY']['inventory_delta'] ['inventory_items'])) self._heartbeat_number += 1 return res def walk_to(self, loc): try: steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY_1", "")) except Exception as e: try: steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY_1", "")) except Exception as e: steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY_3", "")) for step in steps: for i, next_point in enumerate( get_increments(self._posf, step, self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() # self.log.info("Sleeping before next heartbeat") sleep( 2 ) # If you want to make it faster, delete this line... would not recommend though while self.catch_near_pokemon(): sleep( 1 ) # If you want to make it faster, delete this line... would not recommend though def spin_near_fort(self): map_cells = self.nearby_map_objects( )['responses']['GET_MAP_OBJECTS']['map_cells'] forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) destinations = filtered_forts(self._posf, forts) if destinations: fort = destinations[0] # self.log.info("Walking to fort at %s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) position = self._posf # FIXME ? res = self.fort_search(fort_id=fort['id'], fort_latitude=fort['latitude'], fort_longitude=fort['longitude'], player_latitude=position[0], player_longitude=position[1]).call( )['responses']['FORT_SEARCH'] self.log.debug("Fort spinned: %s", res) if 'lure_info' in fort: encounter_id = fort['lure_info']['encounter_id'] fort_id = fort['lure_info']['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call( )['responses']['DISK_ENCOUNTER'] self.disk_encounter_pokemon(fort['lure_info']) return True else: self.log.error("No fort to walk to!") return False def catch_near_pokemon(self): map_cells = self.nearby_map_objects( )['responses']['GET_MAP_OBJECTS']['map_cells'] pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [ (pokemon, distance_in_meters(origin, (pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons ] self.log.debug("Nearby pokemon: : %s", pokemon_distances) for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) self.log.info("Catching Pokemon: %s", self.pokemon_names[str(target[0]['pokemon_id'])]) return self.encounter_pokemon(target[0]) return False def nearby_map_objects(self): position = self.get_position() neighbors = getNeighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0] * len(neighbors), cell_id=neighbors).call() def attempt_catch(self, encounter_id, spawn_point_guid, pokeball): try: r = self.catch_pokemon( normalized_reticle_size=1.950, pokeball=pokeball, spin_modifier=0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_guid=spawn_point_guid, ).call()['responses']['CATCH_POKEMON'] if "status" in r: return r except Exception as e: return False def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses'][ 'GET_INVENTORY']['inventory_delta']['inventory_items'] caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # is a pokemon: pokemon = inventory_item['inventory_item_data']['pokemon_data'] if 'cp' in pokemon and "favorite" not in pokemon: caught_pokemon[pokemon["pokemon_id"]].append(pokemon) elif "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data']['item'] if item['item_id'] in MIN_BAD_ITEM_COUNTS and "count" in item and item[ 'count'] > MIN_BAD_ITEM_COUNTS[item['item_id']]: recycle_count = item['count'] - MIN_BAD_ITEM_COUNTS[ item['item_id']] self.log.info( "Recycling Item_ID {0}, item count {1}".format( item['item_id'], recycle_count)) self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count) for pokemons in caught_pokemon.values(): self.auto_evolve(pokemons, inventory_items) self.auto_release_pokemon(pokemons, self.KEEP_BEST_POKEMON_ONLY) return self.call() def release_single_pokemon(self, pokemon): self.log.debug("Releasing pokemon: %s", pokemon) self.log.info("Releasing pokemon: %s (CP: %s IV: %s)", self.pokemon_names[str(pokemon['pokemon_id'])], pokemon['cp'], pokemonIV(pokemon)) self.release_pokemon(pokemon_id=pokemon["id"]) return True def auto_release_pokemon(self, pokemons, keep_best_only=False): if len(pokemons) > MIN_SIMILAR_POKEMON: if keep_best_only: caught_pokemon_dict = {} for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if pokemon['pokemon_id'] not in caught_pokemon_dict: caught_pokemon_dict[pokemon['pokemon_id']] = 0 if pokemonIV(pokemon) > caught_pokemon_dict[ pokemon['pokemon_id']]: caught_pokemon_dict[pokemon['pokemon_id']] = pokemonIV( pokemon) pokemons = sorted( pokemons, lambda x, y: cmp(x['pokemon_id'], y['pokemon_id'])) for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if pokemonIV(pokemon) < caught_pokemon_dict[ pokemon['pokemon_id']] and ( pokemonIV(pokemon) < self.KEEP_BEST_POKEMON_MIN_IV or pokemon['cp'] < self.KEEP_BEST_POKEMON_MIN_CP): self.release_single_pokemon(pokemon) else: pokemons = sorted(pokemons, lambda x, y: cmp(x['cp'], y['cp']), reverse=True) for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if 'cp' in pokemon and pokemonIVPercentage( pokemon ) < self.MIN_KEEP_IV and pokemon['cp'] < self.KEEP_CP_OVER: self.release_single_pokemon(pokemon) return True def auto_evolve(self, pokemons, inventory_items): for pokemon in pokemons: if pokemon['pokemon_id'] in self.AUTO_EVOLVE_POKEMON_IDS: for inventory_item in inventory_items: if "pokemon_family" in inventory_item[ 'inventory_item_data'] and inventory_item[ 'inventory_item_data']['pokemon_family'][ 'family_id'] == 16 and inventory_item[ 'inventory_item_data'][ 'pokemon_family']['candy'] > 11: self.log.info( "Evolving pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self.evolve_pokemon(pokemon_id=pokemon['id']) return True def disk_encounter_pokemon(self, lureinfo): try: encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call( )['responses']['DISK_ENCOUNTER'] pokeball = 1 if resp['result'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: try: catch_attempt = self.attempt_catch( encounter_id, fort_id, pokeball) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info( "Caught Pokemon: %s (CP: %s)", self.pokemon_names[str( resp['pokemon_data']['pokemon_id'])], resp['pokemon_data']['cp']) sleep( 2 ) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: pokeball += 1 elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info( "Failed to catch Pokemon: %s (CP: %s)", self.pokemon_names[str( resp['pokemon_data']['pokemon_id'])], resp['pokemon_data']['cp']) except Exception as e: pokeball += 1 except Exception as e: self.log.error("Error in disk encounter %s", e) return False sleep( 2 ) # If you want to make it faster, delete this line... would not recommend though def encounter_pokemon(self, pokemon): try: encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] position = self._posf encounter = self.encounter( encounter_id=encounter_id, spawn_point_id=spawn_point_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['ENCOUNTER'] self.log.debug("Started Encounter: %s", encounter) pokeball = 1 if encounter['status'] == 1: capture_status = -1 while capture_status != 0 and capture_status != 3: try: catch_attempt = self.attempt_catch( encounter_id, spawn_point_id, pokeball) capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info( "Caught Pokemon: %s ", self.pokemon_names[str(pokemon['pokemon_id'])]) sleep( 2 ) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: pokeball += 1 elif capture_status != 2: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info( "Failed to Catch Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) sleep( 2 ) # If you want to make it faster, delete this line... would not recommend though except Exception as e: pokeball += 1 except Exception as e: self.log.error("Error in pokemon encounter %s", e) return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance( password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): self.heartbeat() while True: self.heartbeat() sleep( 1 ) # If you want to make it faster, delete this line... would not recommend though self.spin_near_fort() while self.catch_near_pokemon(): sleep( 4 ) # If you want to make it faster, delete this line... would not recommend though pass @staticmethod def flatmap(f, items): return chain.from_iterable(imap(f, items))
class PGoApi: def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None, proxy_config=None): self.set_logger() self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None if provider is not None and ((username is not None and password is not None) or (oauth2_refresh_token is not None)): self.set_authentication(provider, oauth2_refresh_token, username, password) self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") self._position_lat = position_lat self._position_lng = position_lng self._position_alt = position_alt self._signature_lib = None self._session = requests.session() self._session.headers.update({'User-Agent': 'Niantic App'}) self._session.verify = True if proxy_config is not None: self._session.proxies = proxy_config def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None): if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username is not None and password is not None: self._auth_provider.user_login(username, password) else: raise AuthException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token") def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def set_proxy(self, proxy_config): self._session.proxies = proxy_config def get_api_endpoint(self): return self._api_endpoint def set_api_endpoint(self, api_url): if api_url.startswith("https"): self._api_endpoint = api_url else: self._api_endpoint = parse_api_endpoint(api_url) def get_auth_provider(self): return self._auth_provider def create_request(self): request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt) return request def activate_signature(self, lib_path): self._signature_lib = lib_path def get_signature_lib(self): return self._signature_lib def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs ) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def app_simulation_login(self): self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client request = self.create_request() request.get_player() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings(hash="54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5") response = request.call() self.log.info('Finished RPC login sequence (app simulation)') return response """ The login function is not needed anymore but still in the code for backward compatibility" """ def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): if lat is not None and lng is not None and alt is not None: self._position_lat = lat self._position_lng = lng self._position_alt = alt try: self.set_authentication(provider, username=username, password=password) except AuthException as e: self.log.error('Login process failed: %s', e) return False if app_simulation: response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False self.log.info('Login process completed') return True
class PGoApi: def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None, proxy_config=None, device_info=None): self.set_logger() self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None if provider is not None and ((username is not None and password is not None) or (oauth2_refresh_token is not None)): self.set_authentication(provider, oauth2_refresh_token, username, password, proxy_config) self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") self._position_lat = position_lat self._position_lng = position_lng self._position_alt = position_alt self._signature_lib = None self._hash_lib = None self._session = requests.session() self._session.headers.update({'User-Agent': 'Niantic App'}) self._session.verify = True if proxy_config is not None: self._session.proxies = proxy_config self.device_info = device_info def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None, proxy_config=None): if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() elif provider is None: self._auth_provider = None else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if proxy_config is not None: self._auth_provider.set_proxy(proxy_config) if oauth2_refresh_token is not None: self._auth_provider.set_refresh_token(oauth2_refresh_token) elif username is not None and password is not None: if not self._auth_provider.user_login(username, password): raise AuthException("User login failed!") else: raise AuthException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token") def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt=None): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def set_proxy(self, proxy_config): self._session.proxies = proxy_config def get_api_endpoint(self): return self._api_endpoint def set_api_endpoint(self, api_url): if api_url.startswith("https"): self._api_endpoint = api_url else: self._api_endpoint = parse_api_endpoint(api_url) def get_auth_provider(self): return self._auth_provider def create_request(self): request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt, self.device_info) return request def activate_signature(self, signature_lib_path=None, hash_lib_path=None): if signature_lib_path: self.set_signature_lib(signature_lib_path) if hash_lib_path: self.set_hash_lib(hash_lib_path) def set_signature_lib(self, signature_lib_path): self._signature_lib = signature_lib_path def set_hash_lib(self, hash_lib_path): self._hash_lib = hash_lib_path def get_signature_lib(self): return self._signature_lib def get_hash_lib(self): return self._hash_lib def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs ) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError def app_simulation_login(self): self.log.info('Starting RPC login sequence (iOS app simulation)') # Send empty initial request request = self.create_request() response = request.call() time.sleep(1.172) request = self.create_request() response = request.call() time.sleep(1.304) # Send GET_PLAYER only request = self.create_request() request.get_player(player_locale = {'country': 'US', 'language': 'en', 'timezone': 'America/Denver'}) response = request.call() if response.get('responses', {}).get('GET_PLAYER', {}).get('banned', False): raise BannedAccount() time.sleep(1.356) request = self.create_request() request.download_remote_config_version(platform=1, app_version=4500) request.check_challenge() request.get_hatched_eggs() request.get_inventory() request.check_awarded_badges() request.download_settings() response = request.call() time.sleep(1.072) responses = response.get('responses', {}) download_hash = responses.get('DOWNLOAD_SETTINGS', {}).get('hash') inventory = responses.get('GET_INVENTORY', {}).get('inventory_delta', {}) timestamp = inventory.get('new_timestamp_ms') player_level = None for item in inventory.get('inventory_items', []): player_stats = item.get('inventory_item_data', {}).get('player_stats', {}) if player_stats: player_level = player_stats.get('level') break request = self.create_request() request.get_asset_digest(platform=1, app_version=4500) request.check_challenge() request.get_hatched_eggs() request.get_inventory(last_timestamp_ms=timestamp) request.check_awarded_badges() request.download_settings(hash=download_hash) response = request.call() time.sleep(1.709) timestamp = response.get('responses', {}).get('GET_INVENTORY', {}).get('inventory_delta', {}).get('new_timestamp_ms') request = self.create_request() request.get_player_profile() request.check_challenge() request.get_hatched_eggs() request.get_inventory(last_timestamp_ms=timestamp) request.check_awarded_badges() request.download_settings(hash=download_hash) request.get_buddy_walked() response = request.call() time.sleep(1.326) timestamp = response.get('responses', {}).get('GET_INVENTORY', {}).get('inventory_delta', {}).get('new_timestamp_ms') request = self.create_request() request.level_up_rewards(level=player_level) request.check_challenge() request.get_hatched_eggs() request.get_inventory(last_timestamp_ms=timestamp) request.check_awarded_badges() request.download_settings(hash=download_hash) request.get_buddy_walked() response = request.call() self.log.info('Finished RPC login sequence (iOS app simulation)') return response """ The login function is not needed anymore but still in the code for backward compatibility" """ def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): if lat and lng: self._position_lat = lat self._position_lng = lng if alt: self._position_alt = alt try: self.set_authentication(provider, username=username, password=password, proxy_config=self._session.proxies) except AuthException as e: self.log.error('Login process failed: %s', e) return False if app_simulation: response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False challenge_url = response.get('responses', {}).get('CHECK_CHALLENGE', {}).get('challenge_url', ' ') if challenge_url != ' ': self.log.error('CAPCHA required: ' + challenge_url) return challenge_url self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self): self.log = logging.getLogger(__name__) self._auth_provider = None self._api_endpoint = None self._position_lat = 0 self._position_lng = 0 self._position_alt = 0 self._req_method_list = [] def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.info('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException as e: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.info('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i),i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._position_lat = lat self._position_lng = lng self._position_alt = alt def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.info('Create new request...') name = func.upper() if kwargs: self._req_method_list.append( { RequestType.Value(name): kwargs } ) self.log.info("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append( RequestType.Value(name) ) self.log.info("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def login(self, provider, username, password): if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException("Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') # making a standard call, like it is also done by the client self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") response = self.call() if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True
class PGoApi: API_ENTRY = 'https://pgorelease.nianticlabs.com/plfe/rpc' def __init__(self, config, pokemon_names, start_pos): self.log = logging.getLogger(__name__) self._start_pos = start_pos self._walk_count = 1 self._auth_provider = None self._api_endpoint = None self.config = config self.set_position(*start_pos) self._pokeball_type = 1 self.MIN_KEEP_IV = config.get("MIN_KEEP_IV", 0) self.KEEP_CP_OVER = config.get("KEEP_CP_OVER", 0) self.RELEASE_DUPLICATES = config.get("RELEASE_DUPLICATE", 0) self.DUPLICATE_CP_FORGIVENESS = config.get("DUPLICATE_CP_FORGIVENESS", 0) self.MAX_BALL_TYPE = config.get("MAX_BALL_TYPE", 0) self.RANDOM_SLEEP_TIME = config.get("RANDOM_SLEEP_TIME", 0) self._req_method_list = [] self._heartbeat_number = 0 self.pokemon_names = pokemon_names self.pokeballs = [ 0, 0, 0, 0 ] # pokeball counts. set to 0 to force atleast one fort check before trying to capture pokemon self.map_cells = dict() self.min_item_counts = dict( ((getattr(Inventory, key), value) for key, value in config.get('MIN_ITEM_COUNTS', {}).iteritems())) def call(self): if not self._req_method_list: return False if self._auth_provider is None or not self._auth_provider.is_login(): self.log.info('Not logged in') return False player_position = self.get_position() request = RpcApi(self._auth_provider) if self._api_endpoint: api_endpoint = self._api_endpoint else: api_endpoint = self.API_ENTRY self.log.debug('Execution of RPC') response = None try: response = request.request(api_endpoint, self._req_method_list, player_position) except ServerBusyOrOfflineException: self.log.info('Server seems to be busy or offline - try again!') # cleanup after call execution self.log.debug('Cleanup of request!') self._req_method_list = [] return response def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i)) def set_logger(self, logger): self._ = logger or logging.getLogger(__name__) def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) def get_position_raw(self): return self._posf def set_position(self, lat, lng, alt): self.log.debug('Set Position - Lat: %s Long: %s Alt: %s', lat, lng, alt) self._posf = (lat, lng, alt) self._position_lat = f2i(lat) self._position_lng = f2i(lng) self._position_alt = f2i(alt) def __getattr__(self, func): def function(**kwargs): if not self._req_method_list: self.log.debug('Create new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.debug( "Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.debug("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError def heartbeat(self): self.get_player() if self._heartbeat_number % 10 == 0 or self._heartbeat_number == 0: # every 10 heartbeats do a inventory check self.check_awarded_badges() self.get_inventory() res = self.call() if res.get("direction", -1) == 102: self.log.error( "There were a problem responses for api call: %s. Restarting!!!", res) raise AuthException("Token probably expired?") self.log.debug('Heartbeat dictionary: \n\r{}'.format( json.dumps(res, indent=2))) if 'GET_PLAYER' in res['responses']: player_data = res['responses'].get('GET_PLAYER', {}).get('player_data', {}) if os.path.isfile("accounts/%s.json" % self.config['username']): with open("accounts/%s.json" % self.config['username'], "r") as f: file = f.read() json_file = json.loads(file) inventory_items = json_file.get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', []) inventory_items_dict_list = map( lambda x: x.get('inventory_item_data', {}), inventory_items) player_stats = filter(lambda x: 'player_stats' in x, inventory_items_dict_list)[0].get( 'player_stats', {}) else: player_stats = {} currencies = player_data.get('currencies', []) currency_data = ",".join( map( lambda x: "{0}: {1}".format(x.get('name', 'NA'), x.get('amount', 'NA')), currencies)) if 'GET_INVENTORY' in res['responses']: with open("accounts/%s.json" % self.config['username'], "w") as f: res['responses']['lat'] = self._posf[0] res['responses']['lng'] = self._posf[1] f.write(json.dumps(res['responses'], indent=2)) self.log.info("\n List of Pokemon:\n" + get_inventory_data(res, self.pokemon_names) + "\nTotal Pokemon count: " + str(get_pokemon_num(res)) + "\nEgg Hatching status: " + get_incubators_stat(res) + "\n") self.log.info( "\n Username: %s, Lvl: %s, XP: %s/%s \n Currencies: %s \n", player_data.get('username', 'NA'), player_stats.get('level', 'NA'), player_stats.get('experience', 'NA'), player_stats.get('next_level_xp', 'NA'), currency_data) self.log.debug( self.cleanup_inventory(res['responses']['GET_INVENTORY'] ['inventory_delta']['inventory_items'])) self._heartbeat_number += 1 return res def walk_to(self, loc): self._walk_count += 1 steps = get_route(self._posf, loc, self.config.get("USE_GOOGLE", False), self.config.get("GMAPS_API_KEY", "")) for step in steps: for i, next_point in enumerate( get_increments(self._posf, step, self.config.get("STEP_SIZE", 200))): self.set_position(*next_point) self.heartbeat() self.log.info("Sleeping before next heartbeat") sleep( self.RANDOM_SLEEP_TIME * random.random() + 2 ) # If you want to make it faster, delete this line... would not recommend though # make sure we have atleast 1 ball if sum(self.pokeballs) > 0: while self.catch_near_pokemon(): sleep( self.RANDOM_SLEEP_TIME * random.random() + 1 ) # If you want to make it faster, delete this line... would not recommend though # this is in charge of spinning a pokestop def spin_near_fort(self): map_cells = self.nearby_map_objects().get('responses', {}).get( 'GET_MAP_OBJECTS', {}).get('map_cells', {}) forts = PGoApi.flatmap(lambda c: c.get('forts', []), map_cells) if self._start_pos and self._walk_count % self.config.get( "RETURN_START_INTERVAL") == 0: destinations = filtered_forts(self._start_pos, forts) else: destinations = filtered_forts(self._posf, forts) if destinations: destination_num = random.randint(0, min(5, len(destinations) - 1)) fort = destinations[destination_num] self.log.info("Walking to fort at %s,%s", fort['latitude'], fort['longitude']) self.walk_to((fort['latitude'], fort['longitude'])) position = self._posf # FIXME ? res = self.fort_search(fort_id=fort['id'], fort_latitude=fort['latitude'], fort_longitude=fort['longitude'], player_latitude=position[0], player_longitude=position[1]).call( )['responses']['FORT_SEARCH'] self.log.debug("Fort spinned: %s", res) if 'lure_info' in fort: encounter_id = fort['lure_info']['encounter_id'] fort_id = fort['lure_info']['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call( )['responses']['DISK_ENCOUNTER'] self.log.debug('Encounter response is: %s', resp) if self.pokeballs[1] > 9 and self.pokeballs[ 2] > 4 and self.pokeballs[3] > 4: self.disk_encounter_pokemon(fort['lure_info']) return True else: self.log.error("No fort to walk to!") return False # this will catch any nearby pokemon def catch_near_pokemon(self): map_cells = self.nearby_map_objects().get('responses', {}).get( 'GET_MAP_OBJECTS', {}).get('map_cells', {}) pokemons = PGoApi.flatmap(lambda c: c.get('catchable_pokemons', []), map_cells) # cache map cells for api self.map_cells = map_cells # catch first pokemon: origin = (self._posf[0], self._posf[1]) pokemon_distances = [ (pokemon, distance_in_meters(origin, (pokemon['latitude'], pokemon['longitude']))) for pokemon in pokemons ] self.log.debug("Nearby pokemon: : %s", pokemon_distances) for pokemon_distance in pokemon_distances: target = pokemon_distance self.log.debug("Catching pokemon: : %s, distance: %f meters", target[0], target[1]) self.log.info("Catching Pokemon: %s", self.pokemon_names[str(target[0]['pokemon_id'])]) return self.encounter_pokemon(target[0]) if sum(self.pokeballs) == 0: self.spin_near_fort() return False def nearby_map_objects(self): position = self.get_position() neighbors = get_neighbors(self._posf) return self.get_map_objects(latitude=position[0], longitude=position[1], since_timestamp_ms=[0] * len(neighbors), cell_id=neighbors).call() def attempt_catch(self, encounter_id, spawn_point_id, ball_type): r = self.catch_pokemon( normalized_reticle_size=1.950, pokeball=ball_type, spin_modifier=0.850, hit_pokemon=True, normalized_hit_position=1, encounter_id=encounter_id, spawn_point_id=spawn_point_id, ).call()['responses']['CATCH_POKEMON'] self.log.info("Throwing pokeball type: %s", POKEBALLS[ball_type - 1]) # list the pokeball that was thrown if "status" in r: self.log.debug("Status: %d", r['status']) return r def cleanup_inventory(self, inventory_items=None): if not inventory_items: inventory_items = self.get_inventory().call()['responses'][ 'GET_INVENTORY']['inventory_delta']['inventory_items'] all_actual_items = [ xiq['inventory_item_data']["item"] for xiq in inventory_items if "item" in xiq['inventory_item_data'] ] all_actual_item_str = "List of items:\n" all_actual_item_count = 0 all_actual_items = sorted( [x for x in all_actual_items if "count" in x], key=lambda x: x["item_id"]) for xiq in all_actual_items: if 1 <= xiq["item_id"] <= 4: # save counts of pokeballs self.pokeballs[xiq["item_id"]] = xiq["count"] true_item_name = INVENTORY_DICT[xiq["item_id"]] all_actual_item_str += "Item_ID " + str( xiq["item_id"]) + "\titem count " + str( xiq["count"]) + "\t(" + true_item_name + ")\n" all_actual_item_count += xiq["count"] all_actual_item_str += "Total item count: " + str( all_actual_item_count) self.log.info(all_actual_item_str) caught_pokemon = defaultdict(list) for inventory_item in inventory_items: if "pokemon_data" in inventory_item['inventory_item_data']: # This code block checks to see if the inventory item is an item or pokemon pokemon = inventory_item['inventory_item_data']['pokemon_data'] if 'cp' in pokemon and "favorite" not in pokemon: caught_pokemon[pokemon["pokemon_id"]].append(pokemon) elif "item" in inventory_item['inventory_item_data']: item = inventory_item['inventory_item_data'][ 'item'] # Check to see if your holding too many items and recycles them if item['item_id'] in self.min_item_counts and "count" in item and item[ 'count'] > self.min_item_counts[item['item_id']]: recycle_count = item['count'] - self.min_item_counts[ item['item_id']] self.log.info("Recycling {0}, item count {1}".format( INVENTORY_DICT[item['item_id']], recycle_count)) self.recycle_inventory_item(item_id=item['item_id'], count=recycle_count) for pokemons in caught_pokemon.values(): if len( pokemons ) > MIN_SIMILAR_POKEMON: # if you have more than 1 of the same amount of pokemon do this pokemons = sorted(pokemons, lambda x, y: cmp(x['cp'], y['cp']), reverse=True) for pokemon in pokemons: if pokemon['pokemon_id'] in CANDY_NEEDED_TO_EVOLVE: for inventory_item in inventory_items: if "pokemon_family" in inventory_item[ 'inventory_item_data'] and ( inventory_item['inventory_item_data'] ['pokemon_family']['family_id'] == pokemon['pokemon_id'] or inventory_item['inventory_item_data'] ['pokemon_family']['family_id'] == (pokemon['pokemon_id'] - 1) ) and inventory_item['inventory_item_data'][ 'pokemon_family'][ 'candy'] > CANDY_NEEDED_TO_EVOLVE[ pokemon[ 'pokemon_id']]: # Check to see if the pokemon is able to evolve or not, supports t2 evolutions self.log.info( "Evolving pokemon: %s", self.pokemon_names[str( pokemon['pokemon_id'])]) self.evolve_pokemon( pokemon_id=pokemon['id'] ) # quick press ctrl + c to stop the evolution for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if 'cp' in pokemon and pokemon_iv_percentage( pokemon ) < self.MIN_KEEP_IV and pokemon[ "cp"] < self.KEEP_CP_OVER: # remove only if the pokemon is under the IV and CP set up self.log.debug("Releasing pokemon: %s", pokemon) self.log.info( "Releasing pokemon: %s IV: %s", self.pokemon_names[str(pokemon['pokemon_id'])], pokemon_iv_percentage(pokemon)) self.release_pokemon(pokemon_id=pokemon["id"] ) # release the unwanted pokemon if self.RELEASE_DUPLICATES: for pokemons in caught_pokemon.values(): if len(pokemons) > MIN_SIMILAR_POKEMON: pokemons = sorted( pokemons, lambda x, y: cmp( self.pokemon_names[str(x['pokemon_id'])], self. pokemon_names[str(y['pokemon_id'])])) last_pokemon = pokemons[0] for pokemon in pokemons[MIN_SIMILAR_POKEMON:]: if self.pokemon_names[str( pokemon['pokemon_id'])] == self.pokemon_names[ str(last_pokemon['pokemon_id'])]: # Compare two pokemon if the larger IV pokemon has less then DUPLICATE_CP_FORGIVENESS times CP keep it if pokemon_iv_percentage( pokemon) > pokemon_iv_percentage( last_pokemon): if pokemon[ 'cp'] * self.DUPLICATE_CP_FORGIVENESS < last_pokemon[ 'cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", last_pokemon) self.log.info( "Releasing pokemon: %s IV: %s", self.pokemon_names[str( last_pokemon['pokemon_id'])], pokemon_iv_percentage(last_pokemon)) self.release_pokemon( pokemon_id=last_pokemon["id"]) last_pokemon = pokemon else: if last_pokemon[ 'cp'] * self.DUPLICATE_CP_FORGIVENESS > pokemon[ 'cp']: # release the lesser! self.log.debug("Releasing pokemon: %s", pokemon) self.log.info( "Releasing pokemon: %s IV: %s", self.pokemon_names[str( pokemon['pokemon_id'])], pokemon_iv_percentage(pokemon)) self.release_pokemon( pokemon_id=pokemon["id"]) last_pokemon = pokemon return self.call() def disk_encounter_pokemon(self, lureinfo): try: encounter_id = lureinfo['encounter_id'] fort_id = lureinfo['fort_id'] position = self._posf resp = self.disk_encounter(encounter_id=encounter_id, fort_id=fort_id, player_latitude=position[0], player_longitude=position[1]).call( )['responses']['DISK_ENCOUNTER'] if resp['result'] == 1: capture_status = -1 self._pokeball_type = 1 while capture_status != 0 and capture_status != 3: for balls in range(len(self.pokeballs)): self._pokeball_type = balls if self.pokeballs[balls] > 0: catch_attempt = self.attempt_catch( encounter_id, fort_id, self._pokeball_type) self.pokeballs[self._pokeball_type] -= 1 capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) self.log.info( "Caught Pokemon: %s", self.pokemon_names[str( resp['pokemon_data']['pokemon_id'])]) self._pokeball_type = 1 sleep( self.RANDOM_SLEEP_TIME * random.random() + 2 ) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: self.log.info( "Pokemon %s is too wild", self.pokemon_names[str( resp['pokemon_data']['pokemon_id'])]) if self._pokeball_type < self.MAX_BALL_TYPE: self._pokeball_type += 1 elif capture_status == 3: self.log.debug("Failed Catch: : %s", catch_attempt) self.log.info( "Failed to Catch Pokemon: %s", self.pokemon_names[str( resp['pokemon_data']['pokemon_id'])]) self._pokeball_type = 1 sleep( self.RANDOM_SLEEP_TIME * random.random() + 2 ) # If you want to make it faster, delete this line... would not recommend though return False except Exception as e: self.log.error("Error in disk encounter %s", e) self._pokeball_type = 1 return False def encounter_pokemon(self, pokemon): encounter_id = pokemon['encounter_id'] spawn_point_id = pokemon['spawn_point_id'] position = self._posf encounter = self.encounter( encounter_id=encounter_id, spawn_point_id=spawn_point_id, player_latitude=position[0], player_longitude=position[1]).call()['responses']['ENCOUNTER'] # this cade catches pokemon self.log.debug("Started Encounter: %s", encounter) if encounter['status'] == 1: capture_status = -1 self._pokeball_type = 1 # start with a pokeball while capture_status != 0 and capture_status != 3: for balls in range( len(self.pokeballs) ): # try with each ball type starting with weakest self._pokeball_type = balls if self.pokeballs[ balls] > 0: # if you have less then 1 ball do not attempt to catch em all catch_attempt = self.attempt_catch( encounter_id, spawn_point_id, self._pokeball_type) # actual catching code self.pokeballs[ self. _pokeball_type] -= 1 # lowers the thrown ball code capture_status = catch_attempt['status'] if capture_status == 1: self.log.debug("Caught Pokemon: : %s", catch_attempt) # you did it self.log.info( "Caught Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self._pokeball_type = 1 sleep( self.RANDOM_SLEEP_TIME * random.random() + 2 ) # If you want to make it faster, delete this line... would not recommend though return catch_attempt elif capture_status == 2: self.log.info( "Pokemon %s is too wild", self.pokemon_names[str(pokemon['pokemon_id'])]) if self._pokeball_type < self.MAX_BALL_TYPE: self._pokeball_type += 1 # try with a stronger ball elif capture_status == 3: self.log.debug( "Failed Catch: : %s", catch_attempt ) # potential soft ban or just a run away self.log.info( "Failed to Catch Pokemon: %s", self.pokemon_names[str(pokemon['pokemon_id'])]) self._pokeball_type = 1 sleep( self.RANDOM_SLEEP_TIME * random.random() + 2 ) # If you want to make it faster, delete this line... would not recommend though return False def login(self, provider, username, password, cached=False): if not isinstance(username, basestring) or not isinstance( password, basestring): raise AuthException("Username/password not correctly specified") if provider == 'ptc': self._auth_provider = AuthPtc() elif provider == 'google': self._auth_provider = AuthGoogle() else: raise AuthException( "Invalid authentication provider - only ptc/google available.") self.log.debug('Auth provider: %s', provider) if not self._auth_provider.login(username, password): self.log.info('Login process failed') return False self.log.info('Starting RPC login sequence (app simulation)') self.get_player() self.get_hatched_eggs() self.get_inventory() self.check_awarded_badges() self.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930" ) # not sure what this is but dont change it response = self.call() if not response: self.log.info('Login failed!') if os.path.isfile("auth_cache") and cached: response = pickle.load(open("auth_cache")) fname = "auth_cache_%s" % username if os.path.isfile(fname) and cached: response = pickle.load(open(fname)) else: response = self.heartbeat() f = open(fname, "w") pickle.dump(response, f) if not response: self.log.info('Login failed!') return False if 'api_url' in response: self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) self.log.debug('Setting API endpoint to: %s', self._api_endpoint) else: self.log.error('Login failed - unexpected server response!') return False if 'auth_ticket' in response: self._auth_provider.set_ticket(response['auth_ticket'].values()) self.log.info('Finished RPC login sequence (app simulation)') self.log.info('Login process completed') return True def main_loop(self): while True: self.heartbeat() sleep( self.RANDOM_SLEEP_TIME * random.random() + 1 ) # If you want to make it faster, delete this line... would not recommend though if sum(self.pokeballs ) > 0: # if you do not have any balls skip pokemon catching while self.catch_near_pokemon(): sleep( self.RANDOM_SLEEP_TIME * random.random() + 4 ) # If you want to make it faster, delete this line... would not recommend though else: self.log.info( "Less than 1 Poke Balls: Entering pokestops only") self.spin_near_fort() # check local pokestop @staticmethod def flatmap(f, items): return chain.from_iterable(imap(f, items))