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): 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: 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 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): 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._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: 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, 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))
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_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))