def add_new_lap_mysql_api(lap: LapBean) -> bool: lap_time = format_seconds_to_hhmmss(0.0) try: ApiWrapper.put("/driverlaps/", json={'lapName': lap.lapId, 'lapStartDate': lap.lap.lapStartTime, 'lapTime': lap_time, 'UserId': lap.lap.carId, 'TrackId': lap.lap.userId, 'CarId': lap.lap.carId}) return True except Exception as e: print(f"error at add_new_lap_mysql_api : {e}") return False
def _setup_api(self): # instantiate pgoapi @var ApiWrapper self.api = ApiWrapper(config=self.config) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') # send empty map_cells and then our position self.update_web_location()
def retrieveLapRunDataLapQuads(lapId: str) -> []: # config the request for API to get the run data limit, page = int(os.environ['runDataRetrieveLimit']), \ int(os.environ['runDataPaging']) payload = {'lapName': lapId, 'page': page, 'limit': limit} # call API to get runData runData: dict = ApiWrapper.get("/rundata/", params=payload).json()['data'] if len(runData) == 0: raise RunDataException # some times the first rows is mistaken distance data # and need to remove them from the run data ro def removed_first_bad_distance_rows() -> int: glitches, total_rows, g = 0, len(runData), 0 for row_id in range(total_rows - 1): # In this case, the row 'distance' value is bigger then the next row 'distance' value. if runData[row_id]['distance'] > runData[row_id + 1]['distance']: glitches += 1 else: break return glitches # the number of glitches in thr beginning of the lap num_dist_glit: int = removed_first_bad_distance_rows() if num_dist_glit > 0: print(f"FOUND {num_dist_glit} BAD ROWS FOR LAP {lapId}" f"\nLAP FIRST ROWS DISTANCE IS BIGGER THEN THE NEXT ROWS") runData = runData[num_dist_glit:] # remove the rows with the distance glitches in the # beginning # create array of RunDataRow bean object return [RunDataRow(**runData[i]) for i in range(len(runData))]
def __init__(self): self.api = ApiWrapper() self.processed_games = set() self.conn = sqlite3.connect(FILES.SQLLITE_FILE) self._setup_db() self._set_persistance()
def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper(config=self.config) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') # send empty map_cells and then our position self.update_web_location()
def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.' ) self.api = ApiWrapper(config=self.config) self.api.set_position(*position) self.login() self.api.activate_signature(self.get_encryption_lib())
def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper() # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self._print_character_info() self.logger.info('') self.update_inventory() # send empty map_cells and then our position self.update_web_location()
def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.logger.info("Session stale, re-logging in", 'yellow') position = self.position self.api = ApiWrapper() self.position = position self.login()
def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not str(self.api._auth_provider._ticket_expire).isdigit(): self.logger.info("Ticket expired value is not numeric", 'yellow') remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.' ) self.api = ApiWrapper(config=self.config) self.api.set_position(*position) self.login() self.api.activate_signature(self.get_encryption_lib())
class PokemonGoBot(Datastore): @property def position(self): return self.api.actual_lat, self.api.actual_lng, self.api.actual_alt @property def noised_position(self): return self.api.noised_lat, self.api.noised_lng, self.api.noised_alt #@position.setter # these should be called through api now that gps replication is there... #def position(self, position_tuple): # self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple @property def player_data(self): """ Returns the player data as received from the API. :return: The player data. :rtype: dict """ return self._player def __init__(self, config): # Database connection MUST be setup before migrations will work self.database = _init_database('/data/{}.db'.format(config.username)) self.config = config super(PokemonGoBot, self).__init__() self.fort_timeouts = dict() self.pokemon_list = json.load( open(os.path.join(_base_dir, 'data', 'pokemon.json')) ) self.item_list = json.load(open(os.path.join(_base_dir, 'data', 'items.json'))) # @var Metrics self.metrics = Metrics(self) self.latest_inventory = None self.cell = None self.recent_forts = [None] * config.forts_max_circle_size self.tick_count = 0 self.softban = False self.start_position = None self.last_map_object = None self.last_time_map_object = 0 self.logger = logging.getLogger(type(self).__name__) self.alt = self.config.gps_default_altitude # Make our own copy of the workers for this instance self.workers = [] # Theading setup for file writing self.web_update_queue = Queue.Queue(maxsize=1) self.web_update_thread = threading.Thread(target=self.update_web_location_worker) self.web_update_thread.start() # Heartbeat limiting self.heartbeat_threshold = self.config.heartbeat_threshold self.heartbeat_counter = 0 self.last_heartbeat = time.time() self.capture_locked = False # lock catching while moving to VIP pokemon client_id_file_path = os.path.join(_base_dir, 'data', 'mqtt_client_id') saved_info = shelve.open(client_id_file_path) key = 'client_id'.encode('utf-8') if key in saved_info: self.config.client_id = saved_info[key] else: self.config.client_id = str(uuid.uuid4()) saved_info[key] = self.config.client_id saved_info.close() def start(self): self._setup_event_system() self._setup_logging() self.sleep_schedule = SleepSchedule(self, self.config.sleep_schedule) if self.config.sleep_schedule else None if self.sleep_schedule: self.sleep_schedule.work() self._setup_api() self._load_recent_forts() init_inventory(self) self.display_player_info() self._print_character_info() if self.config.pokemon_bag_show_at_start and self.config.pokemon_bag_pokemon_info: self._print_list_pokemon() random.seed() def _setup_event_system(self): handlers = [] if self.config.logging and 'color' in self.config.logging and self.config.logging['color']: handlers.append(ColoredLoggingHandler(self)) else: handlers.append(LoggingHandler(self)) if self.config.enable_social: handlers.append(SocialHandler(self)) if self.config.websocket_server_url: if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner(self.config.websocket_server_url) self.sio_runner.start_listening_async() websocket_handler = SocketIoHandler( self, self.config.websocket_server_url ) handlers.append(websocket_handler) if self.config.websocket_remote_control: remote_control = WebsocketRemoteControl(self).start() # @var EventManager self.event_manager = EventManager(self.config.walker_limit_output, *handlers) self._register_events() if self.config.show_events: self.event_manager.event_report() sys.exit(1) # Registering event: # self.event_manager.register_event("location", parameters=['lat', 'lng']) # # Emitting event should be enough to add logging and send websocket # message: : # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}), def _register_events(self): self.event_manager.register_event( 'location_found', parameters=('position', 'location') ) self.event_manager.register_event('api_error') self.event_manager.register_event('config_error') self.event_manager.register_event('login_started') self.event_manager.register_event('login_failed') self.event_manager.register_event('login_successful') self.event_manager.register_event('set_start_location') self.event_manager.register_event('load_cached_location') self.event_manager.register_event('location_cache_ignored') self.event_manager.register_event('debug') # ignore candy above threshold self.event_manager.register_event( 'ignore_candy_above_thresold', parameters=( 'name', 'amount', 'threshold' ) ) self.event_manager.register_event( 'position_update', parameters=( 'current_position', 'last_position', 'distance', # optional 'distance_unit' # optional ) ) self.event_manager.register_event( 'path_lap_update', parameters=( 'number_lap', 'number_lap_max' ) ) self.event_manager.register_event( 'path_lap_end', parameters=( 'duration', 'resume' ) ) self.event_manager.register_event('location_cache_error') self.event_manager.register_event('bot_start') self.event_manager.register_event('bot_exit') self.event_manager.register_event('bot_interrupted') # sleep stuff self.event_manager.register_event( 'next_sleep', parameters=( 'time', 'duration' ) ) self.event_manager.register_event( 'bot_sleep', parameters=( 'time_hms', 'wake' ) ) # random pause self.event_manager.register_event( 'next_random_pause', parameters=( 'time', 'duration' ) ) self.event_manager.register_event( 'bot_random_pause', parameters=( 'time_hms', 'resume' ) ) # recycle stuff self.event_manager.register_event( 'next_force_recycle', parameters=( 'time' ) ) self.event_manager.register_event('force_recycle') # random alive pause self.event_manager.register_event( 'next_random_alive_pause', parameters=( 'time', 'duration' ) ) self.event_manager.register_event( 'bot_random_alive_pause', parameters=( 'time_hms', 'resume' ) ) # fort stuff self.event_manager.register_event( 'spun_fort', parameters=( 'fort_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'lured_pokemon_found', parameters=( 'fort_id', 'fort_name', 'encounter_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'moving_to_fort', parameters=( 'fort_name', 'distance' ) ) self.event_manager.register_event( 'moving_to_lured_fort', parameters=( 'fort_name', 'distance', 'lure_distance' ) ) self.event_manager.register_event( 'spun_pokestop', parameters=( 'pokestop', 'exp', 'items' ) ) self.event_manager.register_event( 'pokestop_empty', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_out_of_range', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_on_cooldown', parameters=('pokestop', 'minutes_left') ) self.event_manager.register_event( 'unknown_spin_result', parameters=('status_code',) ) self.event_manager.register_event('pokestop_searching_too_often') self.event_manager.register_event('arrived_at_fort') # pokemon stuff self.event_manager.register_event( 'catchable_pokemon', parameters=( 'pokemon_id', 'spawn_point_id', 'encounter_id', 'latitude', 'longitude', 'expiration_timestamp_ms', 'pokemon_name' ) ) self.event_manager.register_event( 'pokemon_appeared', parameters=( 'pokemon', 'ncp', 'cp', 'iv', 'iv_display', 'encounter_id', 'latitude', 'longitude', 'pokemon_id' ) ) self.event_manager.register_event('no_pokeballs') self.event_manager.register_event('enough_ultraballs') self.event_manager.register_event( 'pokemon_catch_rate', parameters=( 'catch_rate', 'ball_name', 'berry_name', 'berry_count' ) ) self.event_manager.register_event( 'threw_berry', parameters=( 'berry_name', 'ball_name', 'new_catch_rate' ) ) self.event_manager.register_event( 'threw_pokeball', parameters=( 'throw_type', 'spin_label', 'ball_name', 'success_percentage', 'count_left' ) ) self.event_manager.register_event( 'pokemon_capture_failed', parameters=('pokemon',) ) self.event_manager.register_event( 'pokemon_vanished', parameters=( 'pokemon', 'encounter_id', 'latitude', 'longitude', 'pokemon_id' ) ) self.event_manager.register_event('pokemon_not_in_range') self.event_manager.register_event('pokemon_inventory_full') self.event_manager.register_event( 'pokemon_caught', parameters=( 'pokemon', 'ncp', 'cp', 'iv', 'iv_display', 'exp', 'encounter_id', 'latitude', 'longitude', 'pokemon_id', 'daily_catch_limit', 'caught_last_24_hour', ) ) self.event_manager.register_event( 'pokemon_evolved', parameters=('pokemon', 'iv', 'cp', 'xp', 'candy') ) self.event_manager.register_event( 'pokemon_upgraded', parameters=('pokemon', 'iv', 'cp', 'candy', 'stardust') ) self.event_manager.register_event('skip_evolve') self.event_manager.register_event('threw_berry_failed', parameters=('status_code',)) self.event_manager.register_event('vip_pokemon') self.event_manager.register_event('gained_candy', parameters=('quantity', 'type')) self.event_manager.register_event('catch_limit') self.event_manager.register_event('show_best_pokemon', parameters=('pokemons')) # level up stuff self.event_manager.register_event( 'level_up', parameters=( 'previous_level', 'current_level' ) ) self.event_manager.register_event( 'level_up_reward', parameters=('items',) ) # lucky egg self.event_manager.register_event( 'used_lucky_egg', parameters=('amount_left',) ) self.event_manager.register_event('lucky_egg_error') # softban self.event_manager.register_event('softban') self.event_manager.register_event('softban_fix') self.event_manager.register_event('softban_fix_done') # egg incubating self.event_manager.register_event( 'incubate_try', parameters=( 'incubator_id', 'egg_id' ) ) self.event_manager.register_event( 'incubate', parameters=('distance_in_km',) ) self.event_manager.register_event( 'next_egg_incubates', parameters=('eggs_left', 'eggs_inc', 'eggs') ) self.event_manager.register_event('incubator_already_used') self.event_manager.register_event('egg_already_incubating') self.event_manager.register_event( 'egg_hatched', parameters=( 'pokemon', 'cp', 'iv', 'exp', 'stardust', 'candy' ) ) # discard item self.event_manager.register_event( 'item_discarded', parameters=( 'amount', 'item', 'maximum' ) ) self.event_manager.register_event( 'item_discard_skipped', parameters=('space',) ) self.event_manager.register_event( 'item_discard_fail', parameters=('item',) ) # inventory self.event_manager.register_event('inventory_full') # release self.event_manager.register_event( 'keep_best_release', parameters=( 'amount', 'pokemon', 'criteria' ) ) self.event_manager.register_event( 'future_pokemon_release', parameters=( 'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic' ) ) self.event_manager.register_event( 'pokemon_release', parameters=('pokemon', 'iv', 'cp', 'candy') ) # polyline walker self.event_manager.register_event( 'polyline_request', parameters=('url',) ) # cluster self.event_manager.register_event( 'found_cluster', parameters=( 'num_points', 'forts', 'radius', 'distance' ) ) self.event_manager.register_event( 'arrived_at_cluster', parameters=( 'num_points', 'forts', 'radius' ) ) # rename self.event_manager.register_event( 'rename_pokemon', parameters=('old_name', 'current_name',) ) self.event_manager.register_event( 'pokemon_nickname_invalid', parameters=('nickname',) ) self.event_manager.register_event( 'unset_pokemon_nickname', parameters=('old_name',) ) # Move To map pokemon self.event_manager.register_event( 'move_to_map_pokemon_fail', parameters=('message',) ) self.event_manager.register_event( 'move_to_map_pokemon_updated_map', parameters=('lat', 'lon') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_to', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_encounter', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_move_towards', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_back', parameters=('last_lat', 'last_lon') ) self.event_manager.register_event( 'moving_to_pokemon_throught_fort', parameters=('fort_name', 'distance','poke_name','poke_dist') ) self.event_manager.register_event( 'move_to_map_pokemon', parameters=('message') ) # cached recent_forts self.event_manager.register_event('loaded_cached_forts') self.event_manager.register_event('cached_fort') self.event_manager.register_event( 'no_cached_forts', parameters=('path', ) ) self.event_manager.register_event( 'error_caching_forts', parameters=('path', ) ) # database shit self.event_manager.register_event('catch_log') self.event_manager.register_event('evolve_log') self.event_manager.register_event('login_log') self.event_manager.register_event('transfer_log') self.event_manager.register_event('pokestop_log') self.event_manager.register_event('softban_log') self.event_manager.register_event( 'badges', parameters=('badge', 'level') ) self.event_manager.register_event( 'player_data', parameters=('player_data', ) ) def tick(self): self.health_record.heartbeat() self.cell = self.get_meta_cell() if self.sleep_schedule: self.sleep_schedule.work() now = time.time() * 1000 for fort in self.cell["forts"]: timeout = fort.get("cooldown_complete_timestamp_ms", 0) if timeout >= now: self.fort_timeouts[fort["id"]] = timeout self.tick_count += 1 # Check if session token has expired self.check_session(self.position) for worker in self.workers: if worker.work() == WorkerResult.RUNNING: return def get_meta_cell(self): location = self.position[0:2] cells = self.find_close_cells(*location) # Combine all cells into a single dict of the items we care about. forts = [] wild_pokemons = [] catchable_pokemons = [] for cell in cells: if "forts" in cell and len(cell["forts"]): forts += cell["forts"] if "wild_pokemons" in cell and len(cell["wild_pokemons"]): wild_pokemons += cell["wild_pokemons"] if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]): catchable_pokemons += cell["catchable_pokemons"] # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved if len(forts) > 1 or not self.cell: return { "forts": forts, "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells. else: return { "forts": self.cell["forts"], "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } def update_web_location(self, cells=[], lat=None, lng=None, alt=None): # we can call the function with no arguments and still get the position # and map_cells if lat is None: lat = self.api._position_lat if lng is None: lng = self.api._position_lng if alt is None: alt = self.api._position_alt # dont cache when teleport_to if self.api.teleporting: return if cells == []: location = self.position[0:2] cells = self.find_close_cells(*location) user_data_cells = os.path.join(_base_dir, 'data', 'cells-%s.json' % self.config.username) try: with open(user_data_cells, 'w') as outfile: json.dump(cells, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_web_location = os.path.join( _base_dir, 'web', 'location-%s.json' % self.config.username ) # alt is unused atm but makes using *location easier try: with open(user_web_location, 'w') as outfile: json.dump({ 'lat': lat, 'lng': lng, 'alt': alt, 'cells': cells }, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_data_lastlocation = os.path.join( _base_dir, 'data', 'last-location-%s.json' % self.config.username ) try: with open(user_data_lastlocation, 'w') as outfile: json.dump({'lat': lat, 'lng': lng, 'alt': alt, 'start_position': self.start_position}, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) def find_close_cells(self, lat, lng): cellid = get_cell_ids(lat, lng) timestamp = [0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get( 'responses', {} ).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) map_cells = [] if status and status == 1: map_cells = map_objects['map_cells'] position = (lat, lng, 0) map_cells.sort( key=lambda x: distance( lat, lng, x['forts'][0]['latitude'], x['forts'][0]['longitude']) if x.get('forts', []) else 1e6 ) return map_cells def _setup_logging(self): log_level = logging.ERROR if self.config.debug: log_level = logging.DEBUG logging.getLogger("requests").setLevel(log_level) logging.getLogger("websocket").setLevel(log_level) logging.getLogger("socketio").setLevel(log_level) logging.getLogger("engineio").setLevel(log_level) logging.getLogger("socketIO-client").setLevel(log_level) logging.getLogger("pgoapi").setLevel(log_level) logging.getLogger("rpc_api").setLevel(log_level) if self.config.logging: logging_format = '%(message)s' logging_format_options = '' if ('show_log_level' not in self.config.logging) or self.config.logging['show_log_level']: logging_format = '[%(levelname)s] ' + logging_format if ('show_process_name' not in self.config.logging) or self.config.logging['show_process_name']: logging_format = '[%(name)10s] ' + logging_format if ('show_datetime' not in self.config.logging) or self.config.logging['show_datetime']: logging_format = '[%(asctime)s] ' + logging_format logging_format_options = '%Y-%m-%d %H:%M:%S' formatter = Formatter(logging_format,logging_format_options) for handler in logging.root.handlers[:]: handler.setFormatter(formatter) def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.' ) self.api = ApiWrapper(config=self.config) self.api.set_position(*position) self.login() self.api.activate_signature(self.get_encryption_lib()) @staticmethod def is_numeric(s): try: float(s) return True except ValueError: return False def login(self): self.event_manager.emit( 'login_started', sender=self, level='info', formatted="Login procedure started." ) lat, lng = self.position[0:2] self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero? while not self.api.login( self.config.auth_service, str(self.config.username), str(self.config.password)): self.event_manager.emit( 'login_failed', sender=self, level='info', formatted="Login error, server busy. Waiting 10 seconds to try again." ) time.sleep(10) with self.database as conn: c = conn.cursor() c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='login'") result = c.fetchone() while True: if result[0] == 1: conn.execute('''INSERT INTO login (timestamp, message) VALUES (?, ?)''', (time.time(), 'LOGIN_SUCCESS')) break else: self.event_manager.emit( 'login_failed', sender=self, level='info', formatted="Login table not founded, skipping log" ) break self.event_manager.emit( 'login_successful', sender=self, level='info', formatted="Login successful." ) def get_encryption_lib(self): if _platform == "Windows" or _platform == "win32": # Check if we are on 32 or 64 bit if sys.maxsize > 2**32: file_name = 'encrypt_64.dll' else: file_name = 'encrypt.dll' else: file_name = 'encrypt.so' if self.config.encrypt_location == '': path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) else: path = self.config.encrypt_location full_path = path + '/'+ file_name if not os.path.isfile(full_path): self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set encrypt_location in config.') self.logger.info('Platform: '+ _platform + ' ' + file_name + ' directory: '+ path) sys.exit(1) else: self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' ' + file_name + ' directory: ' + path) return full_path def _setup_api(self): # instantiate pgoapi @var ApiWrapper self.api = ApiWrapper(config=self.config) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') # send empty map_cells and then our position self.update_web_location() def _print_character_info(self): # get player profile call # ---------------------- response_dict = self.api.get_player() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" if response_dict: self._player = response_dict['responses']['GET_PLAYER']['player_data'] player = self._player else: self.logger.info( "The API didn't return player info, servers are unstable - " "retrying.", 'red' ) sleep(5) self._print_character_info() # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S") pokecoins = '0' stardust = '0' items_inventory = inventory.items() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] self.logger.info('') self.logger.info('--- {username} ---'.format(**player)) self.logger.info( 'Pokemon Bag: {}/{}'.format( inventory.Pokemons.get_space_used(), inventory.get_pokemon_inventory_size() ) ) self.logger.info( 'Items: {}/{}'.format( inventory.Items.get_space_used(), inventory.get_item_inventory_size() ) ) self.logger.info( 'Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins) ) # Items Output self.logger.info( 'PokeBalls: ' + str(items_inventory.get(1).count) + ' | GreatBalls: ' + str(items_inventory.get(2).count) + ' | UltraBalls: ' + str(items_inventory.get(3).count) + ' | MasterBalls: ' + str(items_inventory.get(4).count)) self.logger.info( 'RazzBerries: ' + str(items_inventory.get(701).count) + ' | BlukBerries: ' + str(items_inventory.get(702).count) + ' | NanabBerries: ' + str(items_inventory.get(703).count)) self.logger.info( 'LuckyEgg: ' + str(items_inventory.get(301).count) + ' | Incubator: ' + str(items_inventory.get(902).count) + ' | TroyDisk: ' + str(items_inventory.get(501).count)) self.logger.info( 'Potion: ' + str(items_inventory.get(101).count) + ' | SuperPotion: ' + str(items_inventory.get(102).count) + ' | HyperPotion: ' + str(items_inventory.get(103).count) + ' | MaxPotion: ' + str(items_inventory.get(104).count)) self.logger.info( 'Incense: ' + str(items_inventory.get(401).count) + ' | IncenseSpicy: ' + str(items_inventory.get(402).count) + ' | IncenseCool: ' + str(items_inventory.get(403).count)) self.logger.info( 'Revive: ' + str(items_inventory.get(201).count) + ' | MaxRevive: ' + str(items_inventory.get(202).count)) self.logger.info('') def _print_list_pokemon(self): # get pokemon list bag = inventory.pokemons().all() id_list =list(set(map(lambda x: x.pokemon_id, bag))) id_list.sort() pokemon_list = [filter(lambda x: x.pokemon_id == y, bag) for y in id_list] show_count = self.config.pokemon_bag_show_count show_candies = self.config.pokemon_bag_show_candies poke_info_displayed = self.config.pokemon_bag_pokemon_info def get_poke_info(info, pokemon): poke_info = { 'cp': 'CP {}'.format(pokemon.cp), 'iv_ads': 'A/D/S {}/{}/{}'.format(pokemon.iv_attack, pokemon.iv_defense, pokemon.iv_stamina), 'iv_pct': 'IV {}'.format(pokemon.iv), 'ivcp': 'IVCP {}'.format(round(pokemon.ivcp,2)), 'ncp': 'NCP {}'.format(round(pokemon.cp_percent,2)), 'level': "Level {}".format(pokemon.level), 'hp': 'HP {}/{}'.format(pokemon.hp, pokemon.hp_max), 'moveset': 'Moves: {}'.format(pokemon.moveset), 'dps': 'DPS {}'.format(round(pokemon.moveset.dps, 2)) } if info not in poke_info: raise ConfigException("info '{}' isn't available for displaying".format(info)) return poke_info[info] self.logger.info('Pokemon:') for pokes in pokemon_list: line_p = '#{} {}'.format(pokes[0].pokemon_id, pokes[0].name) if show_count: line_p += '[{}]'.format(len(pokes)) if show_candies: line_p += '[{} candies]'.format(pokes[0].candy_quantity) line_p += ': ' poke_info = ['({})'.format(', '.join([get_poke_info(x, p) for x in poke_info_displayed])) for p in pokes] self.logger.info(line_p + ' | '.join(poke_info)) self.logger.info('') def use_lucky_egg(self): return self.api.use_item_xp_boost(item_id=301) def _set_starting_position(self): self.event_manager.emit( 'set_start_location', sender=self, level='info', formatted='Setting start location.' ) has_position = False if self.config.test: # TODO: Add unit tests return if self.config.location: location_str = self.config.location location = self.get_pos_by_name(location_str.replace(" ", "")) msg = "Location found: {location} {position}" self.event_manager.emit( 'location_found', sender=self, level='info', formatted=msg, data={ 'location': location_str, 'position': location } ) self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='info', formatted="Now at {current_position}", data={ 'current_position': self.position, 'last_position': '', 'distance': '', 'distance_unit': '' } ) self.start_position = self.position has_position = True if self.config.location_cache: try: # save location flag used to pull the last known location from # the location.json self.event_manager.emit( 'load_cached_location', sender=self, level='debug', formatted='Loading cached location...' ) json_file = os.path.join(_base_dir, 'data', 'last-location-%s.json' % self.config.username) try: with open(json_file, "r") as infile: location_json = json.load(infile) except (IOError, ValueError): # Unable to read json file. # File may be corrupt. Create a new one. location_json = [] except: raise FileIOException("Unexpected error reading from {}".web_inventory) location = ( location_json['lat'], location_json['lng'], location_json['alt'], ) # If location has been set in config, only use cache if starting position has not differed if has_position and 'start_position' in location_json: last_start_position = tuple(location_json.get('start_position', [])) # Start position has to have been set on a previous run to do this check if last_start_position and last_start_position != self.start_position: msg = 'Going to a new place, ignoring cached location.' self.event_manager.emit( 'location_cache_ignored', sender=self, level='debug', formatted=msg ) return self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='debug', formatted='Loaded location {current_position} from cache', data={ 'current_position': location, 'last_position': '', 'distance': '', 'distance_unit': '' } ) has_position = True except Exception: if has_position is False: sys.exit( "No cached Location. Please specify initial location." ) self.event_manager.emit( 'location_cache_error', sender=self, level='debug', formatted='Parsing cached location failed.' ) def get_pos_by_name(self, location_name): # Check if given location name, belongs to favorite_locations favorite_location_coords = self._get_pos_by_fav_location(location_name) if favorite_location_coords is not None: return favorite_location_coords # Check if the given location is already a coordinate. if ',' in location_name: possible_coordinates = re.findall( "[-]?\d{1,3}[.]\d{3,7}", location_name ) if len(possible_coordinates) >= 2: # 2 matches, this must be a coordinate. We'll bypass the Google # geocode so we keep the exact location. self.logger.info( '[x] Coordinates found in passed in location, ' 'not geocoding.' ) return float(possible_coordinates[0]), float(possible_coordinates[1]), (float(possible_coordinates[2]) if len(possible_coordinates) == 3 else self.alt) geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) return float(loc.latitude), float(loc.longitude), float(loc.altitude) def _get_pos_by_fav_location(self, location_name): location_name = location_name.lower() coords = None for location in self.config.favorite_locations: if location.get('name').lower() == location_name: coords = re.findall( "[-]?\d{1,3}[.]\d{3,7}", location.get('coords').strip() ) if len(coords) >= 2: self.logger.info('Favorite location found: {} ({})'.format(location_name, coords)) break #TODO: This is real bad if coords is None: return coords else: return float(coords[0]), float(coords[1]), (float(coords[2]) if len(coords) == 3 else self.alt) def heartbeat(self): # Remove forts that we can now spin again. now = time.time() self.fort_timeouts = {id: timeout for id, timeout in self.fort_timeouts.iteritems() if timeout >= now * 1000} if now - self.last_heartbeat >= self.heartbeat_threshold: self.last_heartbeat = now request = self.api.create_request() request.get_player() request.check_awarded_badges() responses = request.call() if responses['responses']['GET_PLAYER']['success'] == True: # we get the player_data anyway, might as well store it self._player = responses['responses']['GET_PLAYER']['player_data'] self.event_manager.emit( 'player_data', sender=self, level='debug', formatted='player_data: {player_data}', data={'player_data': self._player} ) if responses['responses']['CHECK_AWARDED_BADGES']['success'] == True: # store awarded_badges reponse to be used in a task or part of heartbeat self._awarded_badges = responses['responses']['CHECK_AWARDED_BADGES'] if self._awarded_badges.has_key('awarded_badges'): i = 0 for badge in self._awarded_badges['awarded_badges']: badgelevel = self._awarded_badges['awarded_badge_levels'][i] badgename = BadgeType_pb2._BADGETYPE.values_by_number[badge].name i += 1 self.event_manager.emit( 'badges', sender=self, level='info', formatted='awarded badge: {badge}, lvl {level}', data={'badge': badgename, 'level': badgelevel} ) human_behaviour.action_delay(3, 10) inventory.refresh_inventory() try: self.web_update_queue.put_nowait(True) # do this outside of thread every tick except Queue.Full: pass def update_web_location_worker(self): while True: self.web_update_queue.get() self.update_web_location() def display_player_info(self): player_stats = player() if player_stats: nextlvlxp = (int(player_stats.next_level_xp) - int(player_stats.exp)) self.logger.info( 'Level: {}'.format(player_stats.level) + ' (Next Level: {} XP)'.format(nextlvlxp) + ' (Total: {} XP)' ''.format(player_stats.exp)) self.logger.info( 'Pokemon Captured: ' '{}'.format(player_stats.pokemons_captured) + ' | Pokestops Visited: ' '{}'.format(player_stats.poke_stop_visits)) def get_forts(self, order_by_distance=False): forts = [fort for fort in self.cell['forts'] if 'latitude' in fort and 'type' in fort] if order_by_distance: forts.sort(key=lambda x: distance( self.position[0], self.position[1], x['latitude'], x['longitude'] )) return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time() - self.last_time_map_object < self.config.map_object_cache_time: return self.last_map_object self.last_map_object = self.api.get_map_objects( latitude=f2i(lat), longitude=f2i(lng), since_timestamp_ms=timestamp, cell_id=cellid ) self.last_time_map_object = time.time() return self.last_map_object def _load_recent_forts(self): if not self.config.forts_cache_recent_forts: return cached_forts_path = os.path.join(_base_dir, 'data', 'recent-forts-%s.json' % self.config.username) try: # load the cached recent forts cached_recent_forts = [] try: with open(cached_forts_path) as f: cached_recent_forts = json.load(f) except (IOError, ValueError) as e: self.logger.info('[x] Error while opening cached forts: %s' % e, 'red') pass except: raise FileIOException("Unexpected error opening {}".cached_forts_path) num_cached_recent_forts = len(cached_recent_forts) num_recent_forts = len(self.recent_forts) # Handles changes in max_circle_size if not num_recent_forts: self.recent_forts = [] elif num_recent_forts > num_cached_recent_forts: self.recent_forts[-num_cached_recent_forts:] = cached_recent_forts elif num_recent_forts < num_cached_recent_forts: self.recent_forts = cached_recent_forts[-num_recent_forts:] else: self.recent_forts = cached_recent_forts self.event_manager.emit( 'loaded_cached_forts', sender=self, level='debug', formatted='Loaded cached forts...' ) except IOError: self.event_manager.emit( 'no_cached_forts', sender=self, level='debug', formatted='Starting new cached forts for {path}', data={'path': cached_forts_path} )
class PokemonGoBot(Datastore): @property def position(self): return self.api.actual_lat, self.api.actual_lng, self.api.actual_alt @property def noised_position(self): return self.api.noised_lat, self.api.noised_lng, self.api.noised_alt #@position.setter # these should be called through api now that gps replication is there... #def position(self, position_tuple): # self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple @property def player_data(self): """ Returns the player data as received from the API. :return: The player data. :rtype: dict """ return self._player def __init__(self, config): # Database connection MUST be setup before migrations will work self.database = _init_database('/data/{}.db'.format(config.username)) self.config = config super(PokemonGoBot, self).__init__() self.fort_timeouts = dict() self.pokemon_list = json.load( open(os.path.join(_base_dir, 'data', 'pokemon.json')) ) self.item_list = json.load(open(os.path.join(_base_dir, 'data', 'items.json'))) self.metrics = Metrics(self) self.latest_inventory = None self.cell = None self.recent_forts = [None] * config.forts_max_circle_size self.tick_count = 0 self.softban = False self.start_position = None self.last_map_object = None self.last_time_map_object = 0 self.logger = logging.getLogger(type(self).__name__) self.alt = self.config.gps_default_altitude # Make our own copy of the workers for this instance self.workers = [] # Theading setup for file writing self.web_update_queue = Queue.Queue(maxsize=1) self.web_update_thread = threading.Thread(target=self.update_web_location_worker) self.web_update_thread.start() # Heartbeat limiting self.heartbeat_threshold = self.config.heartbeat_threshold self.heartbeat_counter = 0 self.last_heartbeat = time.time() def start(self): self._setup_event_system() self._setup_logging() self._setup_api() self._load_recent_forts() init_inventory(self) self.display_player_info() self._print_character_info() if self.config.pokemon_bag_show_at_start and self.config.pokemon_bag_pokemon_info: self._print_list_pokemon() random.seed() def _setup_event_system(self): handlers = [] if self.config.logging_color: handlers.append(ColoredLoggingHandler()) else: handlers.append(LoggingHandler()) if self.config.enable_social: handlers.append(SocialHandler(self)) if self.config.websocket_server_url: if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner(self.config.websocket_server_url) self.sio_runner.start_listening_async() websocket_handler = SocketIoHandler( self, self.config.websocket_server_url ) handlers.append(websocket_handler) if self.config.websocket_remote_control: remote_control = WebsocketRemoteControl(self).start() self.event_manager = EventManager(*handlers) self._register_events() if self.config.show_events: self.event_manager.event_report() sys.exit(1) # Registering event: # self.event_manager.register_event("location", parameters=['lat', 'lng']) # # Emitting event should be enough to add logging and send websocket # message: : # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}), def _register_events(self): self.event_manager.register_event( 'location_found', parameters=('position', 'location') ) self.event_manager.register_event('api_error') self.event_manager.register_event('config_error') self.event_manager.register_event('login_started') self.event_manager.register_event('login_failed') self.event_manager.register_event('login_successful') self.event_manager.register_event('set_start_location') self.event_manager.register_event('load_cached_location') self.event_manager.register_event('location_cache_ignored') self.event_manager.register_event( 'position_update', parameters=( 'current_position', 'last_position', 'distance', # optional 'distance_unit' # optional ) ) self.event_manager.register_event( 'path_lap_update', parameters=( 'number_lap', 'number_lap_max' ) ) self.event_manager.register_event( 'path_lap_end', parameters=( 'duration', 'resume' ) ) self.event_manager.register_event('location_cache_error') self.event_manager.register_event('bot_start') self.event_manager.register_event('bot_exit') self.event_manager.register_event('bot_interrupted') # sleep stuff self.event_manager.register_event( 'next_sleep', parameters=('time',) ) self.event_manager.register_event( 'bot_sleep', parameters=( 'time_hms', 'wake' ) ) # random pause self.event_manager.register_event( 'next_random_pause', parameters=( 'time', 'duration' ) ) self.event_manager.register_event( 'bot_random_pause', parameters=( 'time_hms', 'resume' ) ) # random alive pause self.event_manager.register_event( 'next_random_alive_pause', parameters=( 'time', 'duration' ) ) self.event_manager.register_event( 'bot_random_alive_pause', parameters=( 'time_hms', 'resume' ) ) # fort stuff self.event_manager.register_event( 'spun_fort', parameters=( 'fort_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'lured_pokemon_found', parameters=( 'fort_id', 'fort_name', 'encounter_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'moving_to_fort', parameters=( 'fort_name', 'distance' ) ) self.event_manager.register_event( 'moving_to_lured_fort', parameters=( 'fort_name', 'distance', 'lure_distance' ) ) self.event_manager.register_event( 'spun_pokestop', parameters=( 'pokestop', 'exp', 'items' ) ) self.event_manager.register_event( 'pokestop_empty', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_out_of_range', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_on_cooldown', parameters=('pokestop', 'minutes_left') ) self.event_manager.register_event( 'unknown_spin_result', parameters=('status_code',) ) self.event_manager.register_event('pokestop_searching_too_often') self.event_manager.register_event('arrived_at_fort') # pokemon stuff self.event_manager.register_event( 'catchable_pokemon', parameters=( 'pokemon_id', 'spawn_point_id', 'encounter_id', 'latitude', 'longitude', 'expiration_timestamp_ms', 'pokemon_name' ) ) self.event_manager.register_event( 'pokemon_appeared', parameters=( 'pokemon', 'ncp', 'cp', 'iv', 'iv_display', 'encounter_id', 'latitude', 'longitude', 'pokemon_id' ) ) self.event_manager.register_event('no_pokeballs') self.event_manager.register_event('enough_ultraballs') self.event_manager.register_event( 'pokemon_catch_rate', parameters=( 'catch_rate', 'ball_name', 'berry_name', 'berry_count' ) ) self.event_manager.register_event( 'threw_berry', parameters=( 'berry_name', 'ball_name', 'new_catch_rate' ) ) self.event_manager.register_event( 'threw_pokeball', parameters=( 'throw_type', 'spin_label', 'ball_name', 'success_percentage', 'count_left' ) ) self.event_manager.register_event( 'pokemon_capture_failed', parameters=('pokemon',) ) self.event_manager.register_event( 'pokemon_vanished', parameters=( 'pokemon', 'encounter_id', 'latitude', 'longitude', 'pokemon_id' ) ) self.event_manager.register_event('pokemon_not_in_range') self.event_manager.register_event('pokemon_inventory_full') self.event_manager.register_event( 'pokemon_caught', parameters=( 'pokemon', 'ncp', 'cp', 'iv', 'iv_display', 'exp', 'encounter_id', 'latitude', 'longitude', 'pokemon_id' ) ) self.event_manager.register_event( 'pokemon_evolved', parameters=('pokemon', 'iv', 'cp', 'xp', 'candy') ) self.event_manager.register_event('skip_evolve') self.event_manager.register_event('threw_berry_failed', parameters=('status_code',)) self.event_manager.register_event('vip_pokemon') self.event_manager.register_event('gained_candy', parameters=('quantity', 'type')) self.event_manager.register_event('catch_limit') # level up stuff self.event_manager.register_event( 'level_up', parameters=( 'previous_level', 'current_level' ) ) self.event_manager.register_event( 'level_up_reward', parameters=('items',) ) # lucky egg self.event_manager.register_event( 'used_lucky_egg', parameters=('amount_left',) ) self.event_manager.register_event('lucky_egg_error') # softban self.event_manager.register_event('softban') self.event_manager.register_event('softban_fix') self.event_manager.register_event('softban_fix_done') # egg incubating self.event_manager.register_event( 'incubate_try', parameters=( 'incubator_id', 'egg_id' ) ) self.event_manager.register_event( 'incubate', parameters=('distance_in_km',) ) self.event_manager.register_event( 'next_egg_incubates', parameters=('eggs_left', 'eggs_inc', 'eggs') ) self.event_manager.register_event('incubator_already_used') self.event_manager.register_event('egg_already_incubating') self.event_manager.register_event( 'egg_hatched', parameters=( 'pokemon', 'cp', 'iv', 'exp', 'stardust', 'candy' ) ) # discard item self.event_manager.register_event( 'item_discarded', parameters=( 'amount', 'item', 'maximum' ) ) self.event_manager.register_event( 'item_discard_skipped', parameters=('space',) ) self.event_manager.register_event( 'item_discard_fail', parameters=('item',) ) # inventory self.event_manager.register_event('inventory_full') # release self.event_manager.register_event( 'keep_best_release', parameters=( 'amount', 'pokemon', 'criteria' ) ) self.event_manager.register_event( 'future_pokemon_release', parameters=( 'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic' ) ) self.event_manager.register_event( 'pokemon_release', parameters=('pokemon', 'iv', 'cp', 'candy') ) # polyline walker self.event_manager.register_event( 'polyline_request', parameters=('url',) ) # cluster self.event_manager.register_event( 'found_cluster', parameters=( 'num_points', 'forts', 'radius', 'distance' ) ) self.event_manager.register_event( 'arrived_at_cluster', parameters=( 'num_points', 'forts', 'radius' ) ) # rename self.event_manager.register_event( 'rename_pokemon', parameters=('old_name', 'current_name',) ) self.event_manager.register_event( 'pokemon_nickname_invalid', parameters=('nickname',) ) self.event_manager.register_event( 'unset_pokemon_nickname', parameters=('old_name',) ) # Move To map pokemon self.event_manager.register_event( 'move_to_map_pokemon_fail', parameters=('message',) ) self.event_manager.register_event( 'move_to_map_pokemon_updated_map', parameters=('lat', 'lon') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_to', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_encounter', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_move_towards', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_back', parameters=('last_lat', 'last_lon') ) self.event_manager.register_event( 'moving_to_pokemon_throught_fort', parameters=('fort_name', 'distance','poke_name','poke_dist') ) # cached recent_forts self.event_manager.register_event('loaded_cached_forts') self.event_manager.register_event('cached_fort') self.event_manager.register_event( 'no_cached_forts', parameters=('path', ) ) self.event_manager.register_event( 'error_caching_forts', parameters=('path', ) ) # database shit self.event_manager.register_event('catch_log') self.event_manager.register_event('evolve_log') self.event_manager.register_event('login_log') self.event_manager.register_event('transfer_log') self.event_manager.register_event('pokestop_log') self.event_manager.register_event('softban_log') def tick(self): self.health_record.heartbeat() self.cell = self.get_meta_cell() now = time.time() * 1000 for fort in self.cell["forts"]: timeout = fort.get("cooldown_complete_timestamp_ms", 0) if timeout >= now: self.fort_timeouts[fort["id"]] = timeout self.tick_count += 1 # Check if session token has expired self.check_session(self.position) for worker in self.workers: if worker.work() == WorkerResult.RUNNING: return def get_meta_cell(self): location = self.position[0:2] cells = self.find_close_cells(*location) # Combine all cells into a single dict of the items we care about. forts = [] wild_pokemons = [] catchable_pokemons = [] for cell in cells: if "forts" in cell and len(cell["forts"]): forts += cell["forts"] if "wild_pokemons" in cell and len(cell["wild_pokemons"]): wild_pokemons += cell["wild_pokemons"] if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]): catchable_pokemons += cell["catchable_pokemons"] # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved if len(forts) > 1 or not self.cell: return { "forts": forts, "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells. else: return { "forts": self.cell["forts"], "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } def update_web_location(self, cells=[], lat=None, lng=None, alt=None): # we can call the function with no arguments and still get the position # and map_cells if lat is None: lat = self.api._position_lat if lng is None: lng = self.api._position_lng if alt is None: alt = self.api._position_alt # dont cache when teleport_to if self.api.teleporting: return if cells == []: location = self.position[0:2] cells = self.find_close_cells(*location) user_data_cells = os.path.join(_base_dir, 'data', 'cells-%s.json' % self.config.username) try: with open(user_data_cells, 'w') as outfile: json.dump(cells, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_web_location = os.path.join( _base_dir, 'web', 'location-%s.json' % self.config.username ) # alt is unused atm but makes using *location easier try: with open(user_web_location, 'w') as outfile: json.dump({ 'lat': lat, 'lng': lng, 'alt': alt, 'cells': cells }, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_data_lastlocation = os.path.join( _base_dir, 'data', 'last-location-%s.json' % self.config.username ) try: with open(user_data_lastlocation, 'w') as outfile: json.dump({'lat': lat, 'lng': lng, 'alt': alt, 'start_position': self.start_position}, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) def find_close_cells(self, lat, lng): cellid = get_cell_ids(lat, lng) timestamp = [0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get( 'responses', {} ).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) map_cells = [] if status and status == 1: map_cells = map_objects['map_cells'] position = (lat, lng, 0) map_cells.sort( key=lambda x: distance( lat, lng, x['forts'][0]['latitude'], x['forts'][0]['longitude']) if x.get('forts', []) else 1e6 ) return map_cells def _setup_logging(self): # log settings # log format if self.config.debug: log_level = logging.DEBUG logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("websocket").setLevel(logging.DEBUG) logging.getLogger("socketio").setLevel(logging.DEBUG) logging.getLogger("engineio").setLevel(logging.DEBUG) logging.getLogger("socketIO-client").setLevel(logging.DEBUG) logging.getLogger("pgoapi").setLevel(logging.DEBUG) logging.getLogger("rpc_api").setLevel(logging.DEBUG) else: log_level = logging.ERROR logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("websocket").setLevel(logging.ERROR) logging.getLogger("socketio").setLevel(logging.ERROR) logging.getLogger("engineio").setLevel(logging.ERROR) logging.getLogger("socketIO-client").setLevel(logging.ERROR) logging.getLogger("pgoapi").setLevel(logging.ERROR) logging.getLogger("rpc_api").setLevel(logging.ERROR) logging.basicConfig( level=log_level, format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s' ) def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.' ) self.api = ApiWrapper(config=self.config) self.api.set_position(*position) self.login() self.api.activate_signature(self.get_encryption_lib()) @staticmethod def is_numeric(s): try: float(s) return True except ValueError: return False def login(self): self.event_manager.emit( 'login_started', sender=self, level='info', formatted="Login procedure started." ) lat, lng = self.position[0:2] self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero? while not self.api.login( self.config.auth_service, str(self.config.username), str(self.config.password)): self.event_manager.emit( 'login_failed', sender=self, level='info', formatted="Login error, server busy. Waiting 10 seconds to try again." ) time.sleep(10) with self.database as conn: c = conn.cursor() c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='login'") result = c.fetchone() while True: if result[0] == 1: conn.execute('''INSERT INTO login (timestamp, message) VALUES (?, ?)''', (time.time(), 'LOGIN_SUCCESS')) break else: self.event_manager.emit( 'login_failed', sender=self, level='info', formatted="Login table not founded, skipping log" ) break self.event_manager.emit( 'login_successful', sender=self, level='info', formatted="Login successful." ) def get_encryption_lib(self): if _platform == "Windows" or _platform == "win32": # Check if we are on 32 or 64 bit if sys.maxsize > 2**32: file_name = 'encrypt_64.dll' else: file_name = 'encrypt.dll' else: file_name = 'encrypt.so' if self.config.encrypt_location == '': path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) else: path = self.config.encrypt_location full_path = path + '/'+ file_name if not os.path.isfile(full_path): self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set encrypt_location in config.') self.logger.info('Platform: '+ _platform + ' ' + file_name + ' directory: '+ path) sys.exit(1) else: self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' ' + file_name + ' directory: ' + path) return full_path def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper(config=self.config) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') # send empty map_cells and then our position self.update_web_location() def _print_character_info(self): # get player profile call # ---------------------- response_dict = self.api.get_player() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" if response_dict: self._player = response_dict['responses']['GET_PLAYER']['player_data'] player = self._player else: self.logger.info( "The API didn't return player info, servers are unstable - " "retrying.", 'red' ) sleep(5) self._print_character_info() # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S") pokecoins = '0' stardust = '0' items_inventory = inventory.items() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] self.logger.info('') self.logger.info('--- {username} ---'.format(**player)) self.logger.info( 'Pokemon Bag: {}/{}'.format( inventory.Pokemons.get_space_used(), inventory.get_pokemon_inventory_size() ) ) self.logger.info( 'Items: {}/{}'.format( inventory.Items.get_space_used(), inventory.get_item_inventory_size() ) ) self.logger.info( 'Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins) ) # Items Output self.logger.info( 'PokeBalls: ' + str(items_inventory.get(1).count) + ' | GreatBalls: ' + str(items_inventory.get(2).count) + ' | UltraBalls: ' + str(items_inventory.get(3).count) + ' | MasterBalls: ' + str(items_inventory.get(4).count)) self.logger.info( 'RazzBerries: ' + str(items_inventory.get(701).count) + ' | BlukBerries: ' + str(items_inventory.get(702).count) + ' | NanabBerries: ' + str(items_inventory.get(703).count)) self.logger.info( 'LuckyEgg: ' + str(items_inventory.get(301).count) + ' | Incubator: ' + str(items_inventory.get(902).count) + ' | TroyDisk: ' + str(items_inventory.get(501).count)) self.logger.info( 'Potion: ' + str(items_inventory.get(101).count) + ' | SuperPotion: ' + str(items_inventory.get(102).count) + ' | HyperPotion: ' + str(items_inventory.get(103).count) + ' | MaxPotion: ' + str(items_inventory.get(104).count)) self.logger.info( 'Incense: ' + str(items_inventory.get(401).count) + ' | IncenseSpicy: ' + str(items_inventory.get(402).count) + ' | IncenseCool: ' + str(items_inventory.get(403).count)) self.logger.info( 'Revive: ' + str(items_inventory.get(201).count) + ' | MaxRevive: ' + str(items_inventory.get(202).count)) self.logger.info('') def _print_list_pokemon(self): # get pokemon list bag = inventory.pokemons().all() id_list =list(set(map(lambda x: x.pokemon_id, bag))) id_list.sort() pokemon_list = [filter(lambda x: x.pokemon_id == y, bag) for y in id_list] show_count = self.config.pokemon_bag_show_count show_candies = self.config.pokemon_bag_show_candies poke_info_displayed = self.config.pokemon_bag_pokemon_info def get_poke_info(info, pokemon): poke_info = { 'cp': 'CP {}'.format(pokemon.cp), 'iv_ads': 'A/D/S {}/{}/{}'.format(pokemon.iv_attack, pokemon.iv_defense, pokemon.iv_stamina), 'iv_pct': 'IV {}'.format(pokemon.iv), 'ivcp': 'IVCP {}'.format(round(pokemon.ivcp,2)), 'ncp': 'NCP {}'.format(round(pokemon.cp_percent,2)), 'level': "Level {}".format(pokemon.level), 'hp': 'HP {}/{}'.format(pokemon.hp, pokemon.hp_max), 'moveset': 'Moves: {}'.format(pokemon.moveset), 'dps': 'DPS {}'.format(round(pokemon.moveset.dps, 2)) } if info not in poke_info: raise ConfigException("info '{}' isn't available for displaying".format(info)) return poke_info[info] self.logger.info('Pokemon:') for pokes in pokemon_list: line_p = '#{} {}'.format(pokes[0].pokemon_id, pokes[0].name) if show_count: line_p += '[{}]'.format(len(pokes)) if show_candies: line_p += '[{} candies]'.format(pokes[0].candy_quantity) line_p += ': ' poke_info = ['({})'.format(', '.join([get_poke_info(x, p) for x in poke_info_displayed])) for p in pokes] self.logger.info(line_p + ' | '.join(poke_info)) self.logger.info('') def use_lucky_egg(self): return self.api.use_item_xp_boost(item_id=301) def _set_starting_position(self): self.event_manager.emit( 'set_start_location', sender=self, level='info', formatted='Setting start location.' ) has_position = False if self.config.test: # TODO: Add unit tests return if self.config.location: location_str = self.config.location location = self.get_pos_by_name(location_str.replace(" ", "")) msg = "Location found: {location} {position}" self.event_manager.emit( 'location_found', sender=self, level='info', formatted=msg, data={ 'location': location_str, 'position': location } ) self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='info', formatted="Now at {current_position}", data={ 'current_position': self.position, 'last_position': '', 'distance': '', 'distance_unit': '' } ) self.start_position = self.position has_position = True if self.config.location_cache: try: # save location flag used to pull the last known location from # the location.json self.event_manager.emit( 'load_cached_location', sender=self, level='debug', formatted='Loading cached location...' ) with open(os.path.join(_base_dir, 'data', 'last-location-%s.json' % self.config.username)) as f: location_json = json.load(f) location = ( location_json['lat'], location_json['lng'], location_json['alt'], ) # If location has been set in config, only use cache if starting position has not differed if has_position and 'start_position' in location_json: last_start_position = tuple(location_json.get('start_position', [])) # Start position has to have been set on a previous run to do this check if last_start_position and last_start_position != self.start_position: msg = 'Going to a new place, ignoring cached location.' self.event_manager.emit( 'location_cache_ignored', sender=self, level='debug', formatted=msg ) return self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='debug', formatted='Loaded location {current_position} from cache', data={ 'current_position': location, 'last_position': '', 'distance': '', 'distance_unit': '' } ) has_position = True except Exception: if has_position is False: sys.exit( "No cached Location. Please specify initial location." ) self.event_manager.emit( 'location_cache_error', sender=self, level='debug', formatted='Parsing cached location failed.' ) def get_pos_by_name(self, location_name): # Check if the given location is already a coordinate. if ',' in location_name: possible_coordinates = re.findall( "[-]?\d{1,3}[.]\d{3,7}", location_name ) if len(possible_coordinates) >= 2: # 2 matches, this must be a coordinate. We'll bypass the Google # geocode so we keep the exact location. self.logger.info( '[x] Coordinates found in passed in location, ' 'not geocoding.' ) return float(possible_coordinates[0]), float(possible_coordinates[1]), (float(possible_coordinates[2]) if len(possible_coordinates) == 3 else self.alt) geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) return float(loc.latitude), float(loc.longitude), float(loc.altitude) def heartbeat(self): # Remove forts that we can now spin again. now = time.time() self.fort_timeouts = {id: timeout for id, timeout in self.fort_timeouts.iteritems() if timeout >= now * 1000} if now - self.last_heartbeat >= self.heartbeat_threshold: self.last_heartbeat = now request = self.api.create_request() request.get_player() request.check_awarded_badges() request.call() try: self.web_update_queue.put_nowait(True) # do this outside of thread every tick except Queue.Full: pass def update_web_location_worker(self): while True: self.web_update_queue.get() self.update_web_location() def display_player_info(self): inventory_items = self.api.get_inventory() inventory_items = inventory_items['responses']['GET_INVENTORY']['inventory_delta']['inventory_items'] player_stats = next((x["inventory_item_data"]["player_stats"] for x in inventory_items if x.get("inventory_item_data", {}).get("player_stats", {})), None) if player_stats: nextlvlxp = (int(player_stats.get('next_level_xp', 0)) - int(player_stats.get('experience', 0))) if 'level' in player_stats and 'experience' in player_stats: self.logger.info( 'Level: {level}'.format( **player_stats) + ' (Next Level: {} XP)'.format( nextlvlxp) + ' (Total: {experience} XP)' ''.format(**player_stats)) if 'pokemons_captured' in player_stats and 'poke_stop_visits' in player_stats: self.logger.info( 'Pokemon Captured: ' '{pokemons_captured}'.format( **player_stats) + ' | Pokestops Visited: ' '{poke_stop_visits}'.format( **player_stats)) def get_forts(self, order_by_distance=False): forts = [fort for fort in self.cell['forts'] if 'latitude' in fort and 'type' in fort] if order_by_distance: forts.sort(key=lambda x: distance( self.position[0], self.position[1], x['latitude'], x['longitude'] )) return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time() - self.last_time_map_object < self.config.map_object_cache_time: return self.last_map_object self.last_map_object = self.api.get_map_objects( latitude=f2i(lat), longitude=f2i(lng), since_timestamp_ms=timestamp, cell_id=cellid ) self.last_time_map_object = time.time() return self.last_map_object def _load_recent_forts(self): if not self.config.forts_cache_recent_forts: return cached_forts_path = os.path.join(_base_dir, 'data', 'recent-forts-%s.json' % self.config.username) try: # load the cached recent forts with open(cached_forts_path) as f: cached_recent_forts = json.load(f) num_cached_recent_forts = len(cached_recent_forts) num_recent_forts = len(self.recent_forts) # Handles changes in max_circle_size if not num_recent_forts: self.recent_forts = [] elif num_recent_forts > num_cached_recent_forts: self.recent_forts[-num_cached_recent_forts:] = cached_recent_forts elif num_recent_forts < num_cached_recent_forts: self.recent_forts = cached_recent_forts[-num_recent_forts:] else: self.recent_forts = cached_recent_forts self.event_manager.emit( 'loaded_cached_forts', sender=self, level='debug', formatted='Loaded cached forts...' ) except IOError: self.event_manager.emit( 'no_cached_forts', sender=self, level='debug', formatted='Starting new cached forts for {path}', data={'path': cached_forts_path} )
class UrfSummonerCrawler(object): def __init__(self): self.api = ApiWrapper() self.processed_games = set() self.conn = sqlite3.connect(FILES.SQLLITE_FILE) self._setup_db() self._set_persistance() def _setup_db(self): self.conn.cursor().execute('''CREATE TABLE IF NOT EXISTS test_data( summ_id integer, champ_id integer, result integer) ''') def _set_persistance(self): """ Sets summoner_list, processed_games_list and processed_summoners_list """ for dat, fname, default in DATS_FILES_AND_DEFAULTS: if os.path.exists(fname): with open(fname, 'rb') as f: setattr(self, dat, pickle.load(f)) else: setattr(self, dat, default) def _save_persistance(self): for dat, fname, _ in DATS_FILES_AND_DEFAULTS: with open(fname, 'wb') as f: pickle.dump(getattr(self, dat), f) def _insert_row(self, summoner, summ_champion, win): self.cur.execute(''' INSERT INTO test_data (summ_id, champ_id, result) VALUES (?, ?, ?) ''', (summoner, summ_champion, win)) def _extract_summ(self, game): summ_team = game['teamId'] win = game['stats']['win'] summ_champion = game['championId'] return (summ_team, win, summ_champion) def _extract_fellow(self, fellow): fsumm = fellow['summonerId'] fchamp = fellow['championId'] fteam = fellow['teamId'] return (fsumm, fchamp, fteam) def crawl(self): self.cur = self.conn.cursor() print(''' Beginning crawl... From previous session (if one existed): Processed games: {} Prcesssed Summoners: {} Summoners left to process: {} (This will grow as execution continues) '''.format(len(self.processed_games), len(self.processed_summs), len(self.summoners))) while self.summoners and len(self.processed_games) <= MAX_GAMES_TO_PROCESS: summoner = self.summoners.popleft() if summoner in self.processed_summs: # skip if we've looked at summ's recent games already continue url = self.api.game_by_summoner(summoner) recent_games = self.api.api_call(url)['games'] for game in recent_games: game_id = game['gameId'] # only parse URF games we haven't seen before if game['subType'] == 'URF' and game_id not in self.processed_games: summ_team, win, summ_champion = self._extract_summ(game) self._insert_row(summoner, summ_champion, win) # Go through each fellow player # and calculate if they won or lost based on if they # are on the current summoners team. for fellow in game['fellowPlayers']: fsumm, fchamp, fteam = self._extract_fellow(fellow) # Winners are fellows on the summoners team if fteam == summ_team: fwin = win else: fwin = not win self._insert_row(fsumm, fchamp, fwin) # Add the summoner to the list of summoners to process later # because they've probably played more urf games if summoner not in self.processed_summs: self.summoners.append(fsumm) self.processed_games.add(game_id) if len(self.processed_games) % 100 == 0: # Every 100 games, write info to db print('Completed ', str(len(self.processed_games)), 'games') self.conn.commit() self._save_persistance() self.processed_summs.add(summoner) self.conn.commit() self._save_persistance() def make_champ_list(self): data = self.api.issue_api_call('https://global.api.pvp.net/api/lol/static-data/na/v1.2/champion?dataById=true&champData=info&api_key=%s' % (API_KEY, )) cur = self.conn.cursor() cur.execute('''CREATE TABLE IF NOT EXISTS champ_data( champ_id integer, name text) ''') cur.execute('DELETE FROM champ_data') for key, value in data["data"].items(): cur.execute(''' INSERT INTO champ_data (champ_id, name) VALUES (?, ?) ''', (int(value["id"]), value["name"])) self.conn.commit()
class PokemonGoBot(object): def __init__(self, config): self.config = config self.pokemon_list = json.load(open('data/pokemon.json')) self.item_list = json.load(open('data/items.json')) def start(self): self._setup_logging() self._setup_api() self.stepper = Stepper(self) random.seed() def take_step(self): self.stepper.take_step() def work_on_cell(self, cell, position, include_fort_on_path): self.check_session(position) if self.config.evolve_all: # Run evolve all once. Flip the bit. print('[#] Attempting to evolve all pokemons ...') worker = EvolveAllWorker(self) worker.work() self.config.evolve_all = [] self._filter_ignored_pokemons(cell) if (self.config.mode == "all" or self.config.mode == "poke") and 'catchable_pokemons' in cell and len(cell[ 'catchable_pokemons']) > 0: logger.log('[#] Something rustles nearby!') # Sort all by distance from current pos- eventually this should # build graph & A* it cell['catchable_pokemons'].sort( key=lambda x: distance(self.position[0], self.position[1], x['latitude'], x['longitude'])) user_web_catchable = 'web/catchable-%s.json' % (self.config.username) for pokemon in cell['catchable_pokemons']: with open(user_web_catchable, 'w') as outfile: json.dump(pokemon, outfile) if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS: break with open(user_web_catchable, 'w') as outfile: json.dump({}, outfile) if (self.config.mode == "all" or self.config.mode == "poke" ) and 'wild_pokemons' in cell and len(cell['wild_pokemons']) > 0: # Sort all by distance from current pos- eventually this should # build graph & A* it cell['wild_pokemons'].sort( key=lambda x: distance(self.position[0], self.position[1], x['latitude'], x['longitude'])) for pokemon in cell['wild_pokemons']: if self.catch_pokemon(pokemon) == PokemonCatchWorker.NO_POKEBALLS: break if (self.config.mode == "all" or self.config.mode == "farm") and include_fort_on_path: if 'forts' in cell: # Only include those with a lat/long forts = [fort for fort in cell['forts'] if 'latitude' in fort and 'type' in fort] gyms = [gym for gym in cell['forts'] if 'gym_points' in gym] # Sort all by distance from current pos- eventually this should # build graph & A* it forts.sort(key=lambda x: distance(self.position[ 0], self.position[1], x['latitude'], x['longitude'])) for fort in forts: worker = MoveToFortWorker(fort, self) worker.work() worker = SeenFortWorker(fort, self) hack_chain = worker.work() if hack_chain > 10: # print('need a rest') break def _setup_logging(self): self.log = logging.getLogger(__name__) # log settings # log format logging.basicConfig( level=logging.DEBUG, format='%(asctime)s [%(module)10s] [%(levelname)5s] %(message)s') if self.config.debug: logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("pgoapi").setLevel(logging.DEBUG) logging.getLogger("rpc_api").setLevel(logging.DEBUG) else: logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("pgoapi").setLevel(logging.ERROR) logging.getLogger("rpc_api").setLevel(logging.ERROR) def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: remaining_time = self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: logger.log("Session stale, re-logging in", 'yellow') self.position = position self.login() def login(self): logger.log('[#] Attempting login to Pokemon Go.', 'white') self.api._auth_token = None self.api._auth_provider = None self.api._api_endpoint = None self.api.set_position(*self.position) while not self.api.login(self.config.auth_service, str(self.config.username), str(self.config.password)): logger.log('[X] Login Error, server busy', 'red') logger.log('[X] Waiting 10 seconds to try again', 'red') time.sleep(10) logger.log('[+] Login to Pokemon Go successful.', 'green') def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper(PGoApi()) # check if the release_config file exists try: with open('release_config.json') as file: pass except: # the file does not exist, warn the user and exit. logger.log( '[#] IMPORTANT: Rename and configure release_config.json.example for your Pokemon release logic first!', 'red') exit(0) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call # get player profile call # ---------------------- self.api.get_player() response_dict = self.api.call() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" player = response_dict['responses']['GET_PLAYER']['player_data'] # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) pokecoins = '0' stardust = '0' balls_stock = self.pokeball_inventory() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] logger.log('[#] Username: {username}'.format(**player)) logger.log('[#] Acccount Creation: {}'.format(creation_date)) logger.log('[#] Bag Storage: {}/{}'.format( self.get_inventory_count('item'), player['max_item_storage'])) logger.log('[#] Pokemon Storage: {}/{}'.format( self.get_inventory_count('pokemon'), player[ 'max_pokemon_storage'])) logger.log('[#] Stardust: {}'.format(stardust)) logger.log('[#] Pokecoins: {}'.format(pokecoins)) logger.log('[#] PokeBalls: ' + str(balls_stock[1])) logger.log('[#] GreatBalls: ' + str(balls_stock[2])) logger.log('[#] UltraBalls: ' + str(balls_stock[3])) self.get_player_info() if self.config.email_status: emailer.periodic_email_status(self) if self.config.initial_transfer: worker = InitialTransferWorker(self) worker.work() logger.log('[#]') self.update_inventory() def catch_pokemon(self, pokemon): worker = PokemonCatchWorker(pokemon, self) return_value = worker.work() if return_value == PokemonCatchWorker.BAG_FULL: logger.log("Bag is full, call EvolveAllWorker first, then raise transfer upperbound and call InitialTransferWorker...", "red") evolve_worker = EvolveAllWorker(self) evolve_worker.work() self.config.initial_transfer = self.config.bag_full_transfer worker = InitialTransferWorker(self) worker.work() return return_value def drop_item(self, item_id, count): self.api.recycle_inventory_item(item_id=item_id, count=count) inventory_req = self.api.call() # Example of good request response #{'responses': {'RECYCLE_INVENTORY_ITEM': {'result': 1, 'new_count': 46}}, 'status_code': 1, 'auth_ticket': {'expire_timestamp_ms': 1469306228058L, 'start': '/HycFyfrT4t2yB2Ij+yoi+on778aymMgxY6RQgvrGAfQlNzRuIjpcnDd5dAxmfoTqDQrbz1m2dGqAIhJ+eFapg==', 'end': 'f5NOZ95a843tgzprJo4W7Q=='}, 'request_id': 8145806132888207460L} return inventory_req def update_inventory(self): self.api.get_inventory() response = self.api.call() self.inventory = list() if 'responses' in response: if 'GET_INVENTORY' in response['responses']: if 'inventory_delta' in response['responses']['GET_INVENTORY']: if 'inventory_items' in response['responses'][ 'GET_INVENTORY']['inventory_delta']: for item in response['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items']: if not 'inventory_item_data' in item: continue if not 'item' in item['inventory_item_data']: continue if not 'item_id' in item['inventory_item_data'][ 'item']: continue if not 'count' in item['inventory_item_data'][ 'item']: continue self.inventory.append(item['inventory_item_data'][ 'item']) def pokeball_inventory(self): self.api.get_player().get_inventory() inventory_req = self.api.call() inventory_dict = inventory_req['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items'] user_web_inventory = 'web/inventory-%s.json' % (self.config.username) with open(user_web_inventory, 'w') as outfile: json.dump(inventory_dict, outfile) # get player balls stock # ---------------------- balls_stock = {1: 0, 2: 0, 3: 0, 4: 0} for item in inventory_dict: try: # print(item['inventory_item_data']['item']) item_id = item['inventory_item_data']['item']['item_id'] item_count = item['inventory_item_data']['item']['count'] if item_id == Item.ITEM_POKE_BALL.value: # print('Poke Ball count: ' + str(item_count)) balls_stock[1] = item_count if item_id == Item.ITEM_GREAT_BALL.value: # print('Great Ball count: ' + str(item_count)) balls_stock[2] = item_count if item_id == Item.ITEM_ULTRA_BALL.value: # print('Ultra Ball count: ' + str(item_count)) balls_stock[3] = item_count except: continue return balls_stock def item_inventory_count(self, id): self.api.get_player().get_inventory() inventory_req = self.api.call() inventory_dict = inventory_req['responses'][ 'GET_INVENTORY']['inventory_delta']['inventory_items'] item_count = 0 for item in inventory_dict: try: if item['inventory_item_data']['item']['item_id'] == int(id): item_count = item[ 'inventory_item_data']['item']['count'] except: continue return item_count def _set_starting_position(self): if self.config.test: # TODO: Add unit tests return if self.config.location: try: location_str = str(self.config.location) location = (self._get_pos_by_name(location_str.replace(" ", ""))) self.position = location self.api.set_position(*self.position) logger.log('') logger.log(u'[x] Address found: {}'.format(self.config.location.decode( 'utf-8'))) logger.log('[x] Position in-game set as: {}'.format(self.position)) logger.log('') return except: logger.log('[x] The location given using -l could not be parsed. Checking for a cached location.') pass if self.config.location_cache and not self.config.location: try: # # save location flag used to pull the last known location from # the location.json with open('data/last-location-%s.json' % (self.config.username)) as f: location_json = json.load(f) self.position = (location_json['lat'], location_json['lng'], 0.0) self.api.set_position(*self.position) logger.log('') logger.log( '[x] Last location flag used. Overriding passed in location') logger.log( '[x] Last in-game location was set as: {}'.format( self.position)) logger.log('') return except: if not self.config.location: sys.exit( "No cached Location. Please specify initial location.") else: pass def _get_pos_by_name(self, location_name): # Check if the given location is already a coordinate. if ',' in location_name: possibleCoordinates = re.findall("[-]?\d{1,3}[.]\d{6,7}", location_name) if len(possibleCoordinates) == 2: # 2 matches, this must be a coordinate. We'll bypass the Google geocode so we keep the exact location. logger.log( '[x] Coordinates found in passed in location, not geocoding.') return (float(possibleCoordinates[0]), float(possibleCoordinates[1]), float("0.0")) geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) # self.log.info('Your given location: %s', loc.address.encode('utf-8')) # self.log.info('lat/long/alt: %s %s %s', loc.latitude, loc.longitude, loc.altitude) return (loc.latitude, loc.longitude, loc.altitude) def _filter_ignored_pokemons(self, cell): process_ignore = False try: with open("./data/catch-ignore.yml", 'r') as y: ignores = yaml.load(y)['ignore'] if len(ignores) > 0: process_ignore = True except Exception, e: pass if process_ignore: # # remove any wild pokemon try: for p in cell['wild_pokemons'][:]: pokemon_id = p['pokemon_data']['pokemon_id'] pokemon_name = filter( lambda x: int(x.get('Number')) == pokemon_id, self.pokemon_list)[0]['Name'] if pokemon_name in ignores: cell['wild_pokemons'].remove(p) except KeyError: pass # # remove catchable pokemon try: for p in cell['catchable_pokemons'][:]: pokemon_id = p['pokemon_id'] pokemon_name = filter( lambda x: int(x.get('Number')) == pokemon_id, self.pokemon_list)[0]['Name'] if pokemon_name in ignores: cell['catchable_pokemons'].remove(p) except KeyError: pass
def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper(PGoApi()) # check if the release_config file exists try: with open('release_config.json') as file: pass except: # the file does not exist, warn the user and exit. logger.log( '[#] IMPORTANT: Rename and configure release_config.json.example for your Pokemon release logic first!', 'red') exit(0) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call # get player profile call # ---------------------- self.api.get_player() response_dict = self.api.call() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" player = response_dict['responses']['GET_PLAYER']['player_data'] # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) pokecoins = '0' stardust = '0' balls_stock = self.pokeball_inventory() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] logger.log('[#] Username: {username}'.format(**player)) logger.log('[#] Acccount Creation: {}'.format(creation_date)) logger.log('[#] Bag Storage: {}/{}'.format( self.get_inventory_count('item'), player['max_item_storage'])) logger.log('[#] Pokemon Storage: {}/{}'.format( self.get_inventory_count('pokemon'), player[ 'max_pokemon_storage'])) logger.log('[#] Stardust: {}'.format(stardust)) logger.log('[#] Pokecoins: {}'.format(pokecoins)) logger.log('[#] PokeBalls: ' + str(balls_stock[1])) logger.log('[#] GreatBalls: ' + str(balls_stock[2])) logger.log('[#] UltraBalls: ' + str(balls_stock[3])) self.get_player_info() if self.config.email_status: emailer.periodic_email_status(self) if self.config.initial_transfer: worker = InitialTransferWorker(self) worker.work() logger.log('[#]') self.update_inventory()
class PokemonGoBot(object): @property def position(self): return self.api._position_lat, self.api._position_lng, 0 def __init__(self, config): self.config = config self.fort_timeouts = dict() self.pokemon_list = json.load( open(os.path.join('data', 'pokemon.json')) ) self.item_list = json.load(open(os.path.join('data', 'items.json'))) self.metrics = Metrics(self) self.latest_inventory = None self.cell = None self.recent_forts = [None] * config.forts_max_circle_size self.tick_count = 0 self.softban = False self.start_position = None self.last_map_object = None self.last_time_map_object = 0 # Make our own copy of the workers for this instance self.workers = [] def start(self): self._setup_logging() self._setup_api() random.seed() def _setup_event_system(self): handlers = [LoggingHandler()] if self.config.websocket_server: websocket_handler = SocketIoHandler(self.config.websocket_server_url) handlers.append(websocket_handler) if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner(self.config.websocket_server_url) self.sio_runner.start_listening_async() self.event_manager = EventManager(*handlers) # Registering event: # self.event_manager.register_event("location", parameters=['lat', 'lng']) # # Emitting event should be enough to add logging and send websocket # message: : # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}), def tick(self): self.cell = self.get_meta_cell() self.tick_count += 1 # Check if session token has expired self.check_session(self.position[0:2]) for worker in self.workers: if worker.work() == WorkerResult.RUNNING: return def get_meta_cell(self): location = self.position[0:2] cells = self.find_close_cells(*location) # Combine all cells into a single dict of the items we care about. forts = [] wild_pokemons = [] catchable_pokemons = [] for cell in cells: if "forts" in cell and len(cell["forts"]): forts += cell["forts"] if "wild_pokemons" in cell and len(cell["wild_pokemons"]): wild_pokemons += cell["wild_pokemons"] if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]): catchable_pokemons += cell["catchable_pokemons"] return { "forts": forts, "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } def update_web_location(self, cells=[], lat=None, lng=None, alt=None): # we can call the function with no arguments and still get the position # and map_cells if lat is None: lat = self.api._position_lat if lng is None: lng = self.api._position_lng if alt is None: alt = 0 if cells == []: cellid = get_cell_ids(lat, lng) timestamp = [0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get( 'responses', {} ).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) cells = map_objects['map_cells'] # insert detail info about gym to fort for cell in cells: if 'forts' in cell: for fort in cell['forts']: if fort.get('type') != 1: self.api.get_gym_details( gym_id=fort.get('id'), player_latitude=lng, player_longitude=lat, gym_latitude=fort.get('latitude'), gym_longitude=fort.get('longitude') ) response_gym_details = self.api.call() fort['gym_details'] = response_gym_details.get( 'responses', {} ).get('GET_GYM_DETAILS', None) user_data_cells = "data/cells-%s.json" % self.config.username with open(user_data_cells, 'w') as outfile: json.dump(cells, outfile) user_web_location = os.path.join( 'web', 'location-%s.json' % self.config.username ) # alt is unused atm but makes using *location easier try: with open(user_web_location, 'w') as outfile: json.dump({ 'lat': lat, 'lng': lng, 'alt': alt, 'cells': cells }, outfile) except IOError as e: logger.log('[x] Error while opening location file: %s' % e, 'red') user_data_lastlocation = os.path.join( 'data', 'last-location-%s.json' % self.config.username ) try: with open(user_data_lastlocation, 'w') as outfile: json.dump({'lat': lat, 'lng': lng, 'start_position': self.start_position}, outfile) except IOError as e: logger.log('[x] Error while opening location file: %s' % e, 'red') def find_close_cells(self, lat, lng): cellid = get_cell_ids(lat, lng) timestamp = [0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get( 'responses', {} ).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) map_cells = [] if status and status == 1: map_cells = map_objects['map_cells'] position = (lat, lng, 0) map_cells.sort( key=lambda x: distance( lat, lng, x['forts'][0]['latitude'], x['forts'][0]['longitude']) if x.get('forts', []) else 1e6 ) return map_cells def _setup_logging(self): self.log = logging.getLogger(__name__) # log settings # log format logging.basicConfig( level=logging.DEBUG, format='%(asctime)s [%(name)10s] [%(levelname)5s] %(message)s') if self.config.debug: logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("websocket").setLevel(logging.DEBUG) logging.getLogger("socketio").setLevel(logging.DEBUG) logging.getLogger("engineio").setLevel(logging.DEBUG) logging.getLogger("socketIO-client").setLevel(logging.DEBUG) logging.getLogger("pgoapi").setLevel(logging.DEBUG) logging.getLogger("rpc_api").setLevel(logging.DEBUG) else: logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("websocket").setLevel(logging.ERROR) logging.getLogger("socketio").setLevel(logging.ERROR) logging.getLogger("engineio").setLevel(logging.ERROR) logging.getLogger("socketIO-client").setLevel(logging.ERROR) logging.getLogger("pgoapi").setLevel(logging.ERROR) logging.getLogger("rpc_api").setLevel(logging.ERROR) def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): logger.log("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: logger.log("Session stale, re-logging in", 'yellow') self.login() @staticmethod def is_numeric(s): try: float(s) return True except ValueError: return False def login(self): logger.log('Attempting login to Pokemon Go.', 'white') self.api.reset_auth() lat, lng = self.position[0:2] self.api.set_position(lat, lng, 0) while not self.api.login(self.config.auth_service, str(self.config.username), str(self.config.password)): logger.log('[X] Login Error, server busy', 'red') logger.log('[X] Waiting 10 seconds to try again', 'red') time.sleep(10) logger.log('Login to Pokemon Go successful.', 'green') def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper(PGoApi()) # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self._print_character_info() logger.log('') self.update_inventory() # send empty map_cells and then our position self.update_web_location() def _print_character_info(self): # get player profile call # ---------------------- self.api.get_player() response_dict = self.api.call() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" if response_dict: self._player = response_dict['responses']['GET_PLAYER']['player_data'] player = self._player else: logger.log( "The API didn't return player info, servers are unstable - " "retrying.", 'red' ) sleep(5) self._print_character_info() # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S") pokecoins = '0' stardust = '0' items_stock = self.current_inventory() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] logger.log('') logger.log('--- {username} ---'.format(**player), 'cyan') self.get_player_info() logger.log( 'Pokemon Bag: {}/{}'.format( self.get_inventory_count('pokemon'), player['max_pokemon_storage'] ), 'cyan' ) logger.log( 'Items: {}/{}'.format( self.get_inventory_count('item'), player['max_item_storage'] ), 'cyan' ) logger.log( 'Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins), 'cyan' ) # Items Output logger.log( 'PokeBalls: ' + str(items_stock[1]) + ' | GreatBalls: ' + str(items_stock[2]) + ' | UltraBalls: ' + str(items_stock[3]), 'cyan') logger.log( 'RazzBerries: ' + str(items_stock[701]) + ' | BlukBerries: ' + str(items_stock[702]) + ' | NanabBerries: ' + str(items_stock[703]), 'cyan') logger.log( 'LuckyEgg: ' + str(items_stock[301]) + ' | Incubator: ' + str(items_stock[902]) + ' | TroyDisk: ' + str(items_stock[501]), 'cyan') logger.log( 'Potion: ' + str(items_stock[101]) + ' | SuperPotion: ' + str(items_stock[102]) + ' | HyperPotion: ' + str(items_stock[103]), 'cyan') logger.log( 'Incense: ' + str(items_stock[401]) + ' | IncenseSpicy: ' + str(items_stock[402]) + ' | IncenseCool: ' + str(items_stock[403]), 'cyan') logger.log( 'Revive: ' + str(items_stock[201]) + ' | MaxRevive: ' + str(items_stock[202]), 'cyan') logger.log('') def use_lucky_egg(self): self.api.use_item_xp_boost(item_id=301) inventory_req = self.api.call() return inventory_req def get_inventory(self): if self.latest_inventory is None: self.api.get_inventory() response = self.api.call() self.latest_inventory = response return self.latest_inventory def update_inventory(self): response = self.get_inventory() self.inventory = list() inventory_items = response.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: for item in inventory_items: item_info = item.get('inventory_item_data', {}).get('item', {}) if {"item_id", "count"}.issubset(set(item_info.keys())): self.inventory.append(item['inventory_item_data']['item']) def current_inventory(self): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items'] user_web_inventory = 'web/inventory-%s.json' % self.config.username with open(user_web_inventory, 'w') as outfile: json.dump(inventory_dict, outfile) # get player items stock # ---------------------- items_stock = {x.value: 0 for x in list(Item)} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_count = item_dict.get('count') item_id = item_dict.get('item_id') if item_count and item_id: if item_id in items_stock: items_stock[item_id] = item_count return items_stock def item_inventory_count(self, id): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses'][ 'GET_INVENTORY']['inventory_delta']['inventory_items'] if id == 'all': return self._all_items_inventory_count(inventory_dict) else: return self._item_inventory_count_per_id(id, inventory_dict) def _item_inventory_count_per_id(self, id, inventory_dict): item_count = 0 for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id == int(id) and item_count: return item_count def _all_items_inventory_count(self, inventory_dict): item_count_dict = {} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id and item_count: item_count_dict[item_id] = item_count return item_count_dict def _set_starting_position(self): has_position = False if self.config.test: # TODO: Add unit tests return if self.config.location: location_str = self.config.location.encode('utf-8') location = (self.get_pos_by_name(location_str.replace(" ", ""))) self.api.set_position(*location) self.start_position = self.position logger.log('') logger.log('Location Found: {}'.format(location_str)) logger.log('GeoPosition: {}'.format(self.position)) logger.log('') has_position = True if self.config.location_cache: try: # save location flag used to pull the last known location from # the location.json logger.log('[x] Parsing cached location...') with open('data/last-location-%s.json' % self.config.username) as f: location_json = json.load(f) location = ( location_json['lat'], location_json['lng'], 0.0 ) # If location has been set in config, only use cache if starting position has not differed if has_position and 'start_position' in location_json: last_start_position = tuple(location_json.get('start_position', [])) # Start position has to have been set on a previous run to do this check if last_start_position and last_start_position != self.start_position: logger.log('[x] Last location flag used but with a stale starting location', 'yellow') logger.log('[x] Using new starting location, {}'.format(self.position)) return self.api.set_position(*location) logger.log('') logger.log( '[x] Last location flag used. Overriding passed in location' ) logger.log( '[x] Last in-game location was set as: {}'.format( self.position ) ) logger.log('') has_position = True except Exception: if has_position is False: sys.exit( "No cached Location. Please specify initial location." ) logger.log( '[x] Parsing cached location failed, try to use the ' 'initial location...' ) def get_pos_by_name(self, location_name): # Check if the given location is already a coordinate. if ',' in location_name: possible_coordinates = re.findall( "[-]?\d{1,3}[.]\d{6,7}", location_name ) if len(possible_coordinates) == 2: # 2 matches, this must be a coordinate. We'll bypass the Google # geocode so we keep the exact location. logger.log( '[x] Coordinates found in passed in location, ' 'not geocoding.' ) return float(possible_coordinates[0]), float(possible_coordinates[1]), float("0.0") geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) return float(loc.latitude), float(loc.longitude), float(loc.altitude) def heartbeat(self): # Remove forts that we can now spin again. self.fort_timeouts = {id: timeout for id, timeout in self.fort_timeouts.iteritems() if timeout >= time.time() * 1000} self.api.get_player() self.api.check_awarded_badges() self.api.call() self.update_web_location() # updates every tick def get_inventory_count(self, what): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: if 'inventory_item_data' in item: if 'pokemon_data' in item['inventory_item_data']: pokecount += 1 itemcount += item['inventory_item_data'].get('item', {}).get('count', 0) if 'pokemon' in what: return pokecount if 'item' in what: return itemcount return '0' def get_player_info(self): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: # print('item {}'.format(item)) playerdata = item.get('inventory_item_data', {}).get('player_stats') if playerdata: nextlvlxp = (int(playerdata.get('next_level_xp', 0)) - int(playerdata.get('experience', 0))) if 'level' in playerdata and 'experience' in playerdata: logger.log( 'Level: {level}'.format( **playerdata) + ' (Next Level: {} XP)'.format( nextlvlxp) + ' (Total: {experience} XP)' ''.format(**playerdata), 'cyan') if 'pokemons_captured' in playerdata and 'poke_stop_visits' in playerdata: logger.log( 'Pokemon Captured: ' '{pokemons_captured}'.format( **playerdata) + ' | Pokestops Visited: ' '{poke_stop_visits}'.format( **playerdata), 'cyan') def has_space_for_loot(self): number_of_things_gained_by_stop = 5 enough_space = ( self.get_inventory_count('item') < self._player['max_item_storage'] - number_of_things_gained_by_stop ) return enough_space def get_forts(self, order_by_distance=False): forts = [fort for fort in self.cell['forts'] if 'latitude' in fort and 'type' in fort] if order_by_distance: forts.sort(key=lambda x: distance( self.position[0], self.position[1], x['latitude'], x['longitude'] )) return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time() - self.last_time_map_object < self.config.map_object_cache_time: return self.last_map_object self.api.get_map_objects( latitude=f2i(lat), longitude=f2i(lng), since_timestamp_ms=timestamp, cell_id=cellid ) self.last_map_object = self.api.call() self.last_time_map_object = time.time() return self.last_map_object
class PokemonGoBot(object): @property def position(self): return self.api._position_lat, self.api._position_lng, 0 @position.setter def position(self, position_tuple): self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple @property def player_data(self): """ Returns the player data as received from the API. :return: The player data. :rtype: dict """ return self._player def __init__(self, config): self.config = config self.fort_timeouts = dict() self.pokemon_list = json.load( open(os.path.join('data', 'pokemon.json')) ) self.item_list = json.load(open(os.path.join('data', 'items.json'))) self.metrics = Metrics(self) self.latest_inventory = None self.cell = None self.recent_forts = [None] * config.forts_max_circle_size self.tick_count = 0 self.softban = False self.start_position = None self.last_map_object = None self.last_time_map_object = 0 self.logger = logging.getLogger(type(self).__name__) # Make our own copy of the workers for this instance self.workers = [] def start(self): self._setup_event_system() self._setup_logging() self._setup_api() random.seed() def _setup_event_system(self): handlers = [LoggingHandler()] if self.config.websocket_server_url: if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner(self.config.websocket_server_url) self.sio_runner.start_listening_async() websocket_handler = SocketIoHandler( self, self.config.websocket_server_url ) handlers.append(websocket_handler) if self.config.websocket_remote_control: remote_control = WebsocketRemoteControl(self).start() self.event_manager = EventManager(*handlers) self._register_events() if self.config.show_events: self.event_manager.event_report() sys.exit(1) # Registering event: # self.event_manager.register_event("location", parameters=['lat', 'lng']) # # Emitting event should be enough to add logging and send websocket # message: : # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}), def _register_events(self): self.event_manager.register_event( 'location_found', parameters=('position', 'location') ) self.event_manager.register_event('api_error') self.event_manager.register_event('config_error') self.event_manager.register_event('login_started') self.event_manager.register_event('login_failed') self.event_manager.register_event('login_successful') self.event_manager.register_event('set_start_location') self.event_manager.register_event('load_cached_location') self.event_manager.register_event('location_cache_ignored') self.event_manager.register_event( 'position_update', parameters=( 'current_position', 'last_position', 'distance', # optional 'distance_unit' # optional ) ) self.event_manager.register_event('location_cache_error') self.event_manager.register_event('bot_start') self.event_manager.register_event('bot_exit') # sleep stuff self.event_manager.register_event( 'next_sleep', parameters=('time',) ) self.event_manager.register_event( 'bot_sleep', parameters=('time_in_seconds',) ) # fort stuff self.event_manager.register_event( 'spun_fort', parameters=( 'fort_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'lured_pokemon_found', parameters=( 'fort_id', 'fort_name', 'encounter_id', 'latitude', 'longitude' ) ) self.event_manager.register_event( 'moving_to_fort', parameters=( 'fort_name', 'distance' ) ) self.event_manager.register_event( 'moving_to_lured_fort', parameters=( 'fort_name', 'distance', 'lure_distance' ) ) self.event_manager.register_event( 'spun_pokestop', parameters=( 'pokestop', 'exp', 'items' ) ) self.event_manager.register_event( 'pokestop_empty', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_out_of_range', parameters=('pokestop',) ) self.event_manager.register_event( 'pokestop_on_cooldown', parameters=('pokestop', 'minutes_left') ) self.event_manager.register_event( 'unknown_spin_result', parameters=('status_code',) ) self.event_manager.register_event('pokestop_searching_too_often') self.event_manager.register_event('arrived_at_fort') # pokemon stuff self.event_manager.register_event( 'catchable_pokemon', parameters=( 'pokemon_id', 'spawn_point_id', 'encounter_id', 'latitude', 'longitude', 'expiration_timestamp_ms' ) ) self.event_manager.register_event( 'pokemon_appeared', parameters=( 'pokemon', 'cp', 'iv', 'iv_display', ) ) self.event_manager.register_event( 'pokemon_catch_rate', parameters=( 'catch_rate', 'berry_name', 'berry_count' ) ) self.event_manager.register_event( 'threw_berry', parameters=( 'berry_name', 'new_catch_rate' ) ) self.event_manager.register_event( 'threw_pokeball', parameters=( 'pokeball', 'success_percentage', 'count_left' ) ) self.event_manager.register_event( 'pokemon_fled', parameters=('pokemon',) ) self.event_manager.register_event( 'pokemon_vanished', parameters=('pokemon',) ) self.event_manager.register_event( 'pokemon_caught', parameters=( 'pokemon', 'cp', 'iv', 'iv_display', 'exp' ) ) self.event_manager.register_event( 'pokemon_evolved', parameters=('pokemon', 'iv', 'cp') ) self.event_manager.register_event( 'pokemon_evolve_fail', parameters=('pokemon',) ) self.event_manager.register_event('skip_evolve') self.event_manager.register_event('threw_berry_failed', parameters=('status_code',)) self.event_manager.register_event('vip_pokemon') # level up stuff self.event_manager.register_event( 'level_up', parameters=( 'previous_level', 'current_level' ) ) self.event_manager.register_event( 'level_up_reward', parameters=('items',) ) # lucky egg self.event_manager.register_event( 'used_lucky_egg', parameters=('amount_left',) ) self.event_manager.register_event('lucky_egg_error') # softban self.event_manager.register_event('softban') self.event_manager.register_event('softban_fix') self.event_manager.register_event('softban_fix_done') # egg incubating self.event_manager.register_event( 'incubate_try', parameters=( 'incubator_id', 'egg_id' ) ) self.event_manager.register_event( 'incubate', parameters=('distance_in_km',) ) self.event_manager.register_event( 'next_egg_incubates', parameters=('distance_in_km',) ) self.event_manager.register_event('incubator_already_used') self.event_manager.register_event('egg_already_incubating') self.event_manager.register_event( 'egg_hatched', parameters=( 'pokemon', 'cp', 'iv', 'exp', 'stardust', 'candy' ) ) # discard item self.event_manager.register_event( 'item_discarded', parameters=( 'amount', 'item', 'maximum' ) ) self.event_manager.register_event( 'item_discard_fail', parameters=('item',) ) # inventory self.event_manager.register_event('inventory_full') # release self.event_manager.register_event( 'keep_best_release', parameters=( 'amount', 'pokemon', 'criteria' ) ) self.event_manager.register_event( 'future_pokemon_release', parameters=( 'pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic' ) ) self.event_manager.register_event( 'pokemon_release', parameters=('pokemon', 'cp', 'iv') ) # polyline walker self.event_manager.register_event( 'polyline_request', parameters=('url',) ) # cluster self.event_manager.register_event( 'found_cluster', parameters=( 'num_points', 'forts', 'radius', 'distance' ) ) self.event_manager.register_event( 'arrived_at_cluster', parameters=( 'forts', 'radius' ) ) # rename self.event_manager.register_event( 'rename_pokemon', parameters=( 'old_name', 'current_name' ) ) self.event_manager.register_event( 'pokemon_nickname_invalid', parameters=('nickname',) ) self.event_manager.register_event('unset_pokemon_nickname') # Move To map pokemon self.event_manager.register_event( 'move_to_map_pokemon_fail', parameters=('message',) ) self.event_manager.register_event( 'move_to_map_pokemon_updated_map', parameters=('lat', 'lon') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_to', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_encounter', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_move_towards', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in') ) self.event_manager.register_event( 'move_to_map_pokemon_teleport_back', parameters=('last_lat', 'last_lon') ) def tick(self): self.health_record.heartbeat() self.cell = self.get_meta_cell() self.tick_count += 1 # Check if session token has expired self.check_session(self.position[0:2]) for worker in self.workers: if worker.work() == WorkerResult.RUNNING: return def open_bag(self): response = self.get_inventory() self.inventory = list() own_pokemon_list = { int(x['Number']): {'name': x['Name'], 'candies_needed': x.get('Next Evolution Requirements', {'Amount': 18446744073709551615 }).get('Amount'), 'count': 0} for x in self.pokemon_list } inventory_items = response.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: for item in inventory_items: item_info = item.get('inventory_item_data', {}) if {"pokemon_family"}.issubset(set(item_info.keys())): pokemon_family = item['inventory_item_data']['pokemon_family'] pokemon_data = own_pokemon_list[pokemon_family['family_id']] pokemon_data['candies_number'] = pokemon_family['candy'] if {"pokemon_data"}.issubset(set(item_info.keys())): pokemon_id = item['inventory_item_data']['pokemon_data'].get('pokemon_id') if pokemon_id: pokemon = own_pokemon_list[pokemon_id] pokemon['count'] += 1 with open('pokemons_process.csv', 'wb') as csvfile: spamwriter = csv.writer( csvfile, delimiter=str(u';'), quotechar=str(u'"'), quoting=csv.QUOTE_MINIMAL ) spamwriter.writerow([ 'pokemon', 'evolution_cost', 'total_candies', 'total_evolutions', 'pokemons_available', 'evolutions_available', 'advice' ]) advice = { True:'{} evolutions are unused. Catch more. Relax restrictions for this pokemon in config.json ("release" field)', False: '{} pokemons are unused. Release more. Enhance restrictions for this pokemon in config.json ("release" field)' } total_upgrade_count = 0 for id, pokemon_info in own_pokemon_list.iteritems(): pokemon_count = pokemon_info['count'] current_candies_number = pokemon_info.get('candies_number', 0) candies_needed = pokemon_info['candies_needed'] if pokemon_count or (current_candies_number): if candies_needed == 18446744073709551615: spamwriter.writerow([ pokemon_info['name'], 'last form', current_candies_number, '-', pokemon_count, '-', advice[False].format(pokemon_count) ]) else: # if count and (candies_needed != 18446744073709551615): current_candies_number = pokemon_info.get('candies_number', 0) count_upgrade = current_candies_number/pokemon_info['candies_needed'] total_upgrade_count += min(pokemon_count, count_upgrade) candies_left = candies_needed - candies_needed*current_candies_number pokemons_left = pokemon_count - total_upgrade_count if candies_left: advice_string = advice[True].format(candies_left) else: advice_string = advice[False].format(pokemons_left) spamwriter.writerow([ pokemon_info['name'], candies_needed, current_candies_number, count_upgrade, pokemon_count, total_upgrade_count, advice_string ]) spamwriter.writerow([ 'Total evolutons can be made: {}'.format(total_upgrade_count) ]) def get_meta_cell(self): location = self.position[0:2] cells = self.find_close_cells(*location) # Combine all cells into a single dict of the items we care about. forts = [] wild_pokemons = [] catchable_pokemons = [] for cell in cells: if "forts" in cell and len(cell["forts"]): forts += cell["forts"] if "wild_pokemons" in cell and len(cell["wild_pokemons"]): wild_pokemons += cell["wild_pokemons"] if "catchable_pokemons" in cell and len(cell["catchable_pokemons"]): catchable_pokemons += cell["catchable_pokemons"] # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved if len(forts) > 1 or not self.cell: return { "forts": forts, "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells. else: return { "forts": self.cell["forts"], "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } def update_web_location(self, cells=[], lat=None, lng=None, alt=None): # we can call the function with no arguments and still get the position # and map_cells if lat is None: lat = self.api._position_lat if lng is None: lng = self.api._position_lng if alt is None: alt = 0 if cells == []: location = self.position[0:2] cells = self.find_close_cells(*location) # insert detail info about gym to fort for cell in cells: if 'forts' in cell: for fort in cell['forts']: if fort.get('type') != 1: response_gym_details = self.api.get_gym_details( gym_id=fort.get('id'), player_latitude=lng, player_longitude=lat, gym_latitude=fort.get('latitude'), gym_longitude=fort.get('longitude') ) fort['gym_details'] = response_gym_details.get( 'responses', {} ).get('GET_GYM_DETAILS', None) user_data_cells = "data/cells-%s.json" % self.config.username with open(user_data_cells, 'w') as outfile: json.dump(cells, outfile) user_web_location = os.path.join( 'web', 'location-%s.json' % self.config.username ) # alt is unused atm but makes using *location easier try: with open(user_web_location, 'w') as outfile: json.dump({ 'lat': lat, 'lng': lng, 'alt': alt, 'cells': cells }, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_data_lastlocation = os.path.join( 'data', 'last-location-%s.json' % self.config.username ) try: with open(user_data_lastlocation, 'w') as outfile: json.dump({'lat': lat, 'lng': lng, 'start_position': self.start_position}, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) def find_close_cells(self, lat, lng): cellid = get_cell_ids(lat, lng) timestamp = [0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get( 'responses', {} ).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) map_cells = [] if status and status == 1: map_cells = map_objects['map_cells'] position = (lat, lng, 0) map_cells.sort( key=lambda x: distance( lat, lng, x['forts'][0]['latitude'], x['forts'][0]['longitude']) if x.get('forts', []) else 1e6 ) return map_cells def _setup_logging(self): # log settings # log format if self.config.debug: log_level = logging.DEBUG logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("websocket").setLevel(logging.DEBUG) logging.getLogger("socketio").setLevel(logging.DEBUG) logging.getLogger("engineio").setLevel(logging.DEBUG) logging.getLogger("socketIO-client").setLevel(logging.DEBUG) logging.getLogger("pgoapi").setLevel(logging.DEBUG) logging.getLogger("rpc_api").setLevel(logging.DEBUG) else: log_level = logging.ERROR logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("websocket").setLevel(logging.ERROR) logging.getLogger("socketio").setLevel(logging.ERROR) logging.getLogger("engineio").setLevel(logging.ERROR) logging.getLogger("socketIO-client").setLevel(logging.ERROR) logging.getLogger("pgoapi").setLevel(logging.ERROR) logging.getLogger("rpc_api").setLevel(logging.ERROR) logging.basicConfig( level=log_level, format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s' ) def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.' ) position = self.position self.api = ApiWrapper() self.position = position self.login() self.api.activate_signature(self.get_encryption_lib()) @staticmethod def is_numeric(s): try: float(s) return True except ValueError: return False def login(self): self.event_manager.emit( 'login_started', sender=self, level='info', formatted="Login procedure started." ) lat, lng = self.position[0:2] self.api.set_position(lat, lng, 0) while not self.api.login( self.config.auth_service, str(self.config.username), str(self.config.password)): self.event_manager.emit( 'login_failed', sender=self, level='info', formatted="Login error, server busy. Waiting 10 seconds to try again." ) time.sleep(10) self.event_manager.emit( 'login_successful', sender=self, level='info', formatted="Login successful." ) def get_encryption_lib(self): if _platform == "linux" or _platform == "linux2" or _platform == "darwin": file_name = 'encrypt.so' elif _platform == "Windows" or _platform == "win32": # Check if we are on 32 or 64 bit if sys.maxsize > 2**32: file_name = 'encrypt_64.dll' else: file_name = 'encrypt.dll' if self.config.encrypt_location == '': path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) else: path = self.config.encrypt_location full_path = path + '/'+ file_name if not os.path.isfile(full_path): self.logger.error(file_name + ' is not found! Please place it in the bots root directory or set libencrypt_location in config.') self.logger.info('Platform: '+ _platform + ' Encrypt.so directory: '+ path) sys.exit(1) else: self.logger.info('Found '+ file_name +'! Platform: ' + _platform + ' Encrypt.so directory: ' + path) return full_path def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper() # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self._print_character_info() self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') self.update_inventory() # send empty map_cells and then our position self.update_web_location() self.open_bag() def _print_character_info(self): # get player profile call # ---------------------- response_dict = self.api.get_player() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" if response_dict: self._player = response_dict['responses']['GET_PLAYER']['player_data'] player = self._player else: self.logger.info( "The API didn't return player info, servers are unstable - " "retrying.", 'red' ) sleep(5) self._print_character_info() # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S") pokecoins = '0' stardust = '0' items_stock = self.current_inventory() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] self.logger.info('') self.logger.info('--- {username} ---'.format(**player)) self.get_player_info() self.logger.info( 'Pokemon Bag: {}/{}'.format( self.get_inventory_count('pokemon'), player['max_pokemon_storage'] ) ) self.logger.info( 'Items: {}/{}'.format( self.get_inventory_count('item'), player['max_item_storage'] ) ) self.logger.info( 'Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins) ) # Items Output self.logger.info( 'PokeBalls: ' + str(items_stock[1]) + ' | GreatBalls: ' + str(items_stock[2]) + ' | UltraBalls: ' + str(items_stock[3])) self.logger.info( 'RazzBerries: ' + str(items_stock[701]) + ' | BlukBerries: ' + str(items_stock[702]) + ' | NanabBerries: ' + str(items_stock[703])) self.logger.info( 'LuckyEgg: ' + str(items_stock[301]) + ' | Incubator: ' + str(items_stock[902]) + ' | TroyDisk: ' + str(items_stock[501])) self.logger.info( 'Potion: ' + str(items_stock[101]) + ' | SuperPotion: ' + str(items_stock[102]) + ' | HyperPotion: ' + str(items_stock[103]) + ' | MaxPotion: ' + str(items_stock[104])) self.logger.info( 'Incense: ' + str(items_stock[401]) + ' | IncenseSpicy: ' + str(items_stock[402]) + ' | IncenseCool: ' + str(items_stock[403])) self.logger.info( 'Revive: ' + str(items_stock[201]) + ' | MaxRevive: ' + str(items_stock[202])) self.logger.info('') def use_lucky_egg(self): return self.api.use_item_xp_boost(item_id=301) def get_inventory(self): if self.latest_inventory is None: self.latest_inventory = self.api.get_inventory() return self.latest_inventory def update_inventory(self): response = self.get_inventory() self.inventory = list() inventory_items = response.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: for item in inventory_items: item_info = item.get('inventory_item_data', {}).get('item', {}) if {"item_id", "count"}.issubset(set(item_info.keys())): self.inventory.append(item['inventory_item_data']['item']) def current_inventory(self): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items'] user_web_inventory = 'web/inventory-%s.json' % self.config.username with open(user_web_inventory, 'w') as outfile: json.dump(inventory_dict, outfile) # get player items stock # ---------------------- items_stock = {x.value: 0 for x in list(Item)} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_count = item_dict.get('count') item_id = item_dict.get('item_id') if item_count and item_id: if item_id in items_stock: items_stock[item_id] = item_count return items_stock def item_inventory_count(self, id): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses'][ 'GET_INVENTORY']['inventory_delta']['inventory_items'] if id == 'all': return self._all_items_inventory_count(inventory_dict) else: return self._item_inventory_count_per_id(id, inventory_dict) def _item_inventory_count_per_id(self, id, inventory_dict): item_count = 0 for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id == int(id) and item_count: return item_count return 0 def _all_items_inventory_count(self, inventory_dict): item_count_dict = {} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id and item_count: item_count_dict[item_id] = item_count return item_count_dict def _set_starting_position(self): self.event_manager.emit( 'set_start_location', sender=self, level='info', formatted='Setting start location.' ) has_position = False if self.config.test: # TODO: Add unit tests return if self.config.location: location_str = self.config.location location = self.get_pos_by_name(location_str.replace(" ", "")) msg = "Location found: {location} {position}" self.event_manager.emit( 'location_found', sender=self, level='info', formatted=msg, data={ 'location': location_str, 'position': location } ) self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='info', formatted="Now at {current_position}", data={ 'current_position': self.position, 'last_position': '', 'distance': '', 'distance_unit': '' } ) self.start_position = self.position has_position = True if self.config.location_cache: try: # save location flag used to pull the last known location from # the location.json self.event_manager.emit( 'load_cached_location', sender=self, level='debug', formatted='Loading cached location...' ) with open('data/last-location-%s.json' % self.config.username) as f: location_json = json.load(f) location = ( location_json['lat'], location_json['lng'], 0.0 ) # If location has been set in config, only use cache if starting position has not differed if has_position and 'start_position' in location_json: last_start_position = tuple(location_json.get('start_position', [])) # Start position has to have been set on a previous run to do this check if last_start_position and last_start_position != self.start_position: msg = 'Going to a new place, ignoring cached location.' self.event_manager.emit( 'location_cache_ignored', sender=self, level='debug', formatted=msg ) return self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='debug', formatted='Loaded location {current_position} from cache', data={ 'current_position': location, 'last_position': '', 'distance': '', 'distance_unit': '' } ) has_position = True except Exception: if has_position is False: sys.exit( "No cached Location. Please specify initial location." ) self.event_manager.emit( 'location_cache_error', sender=self, level='debug', formatted='Parsing cached location failed.' ) def get_pos_by_name(self, location_name): # Check if the given location is already a coordinate. if ',' in location_name: possible_coordinates = re.findall( "[-]?\d{1,3}[.]\d{3,7}", location_name ) if len(possible_coordinates) == 2: # 2 matches, this must be a coordinate. We'll bypass the Google # geocode so we keep the exact location. self.logger.info( '[x] Coordinates found in passed in location, ' 'not geocoding.' ) return float(possible_coordinates[0]), float(possible_coordinates[1]), float("0.0") geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) return float(loc.latitude), float(loc.longitude), float(loc.altitude) def heartbeat(self): # Remove forts that we can now spin again. self.fort_timeouts = {id: timeout for id, timeout in self.fort_timeouts.iteritems() if timeout >= time.time() * 1000} request = self.api.create_request() request.get_player() request.check_awarded_badges() request.call() self.update_web_location() # updates every tick def get_inventory_count(self, what): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: if 'inventory_item_data' in item: if 'pokemon_data' in item['inventory_item_data']: pokecount += 1 itemcount += item['inventory_item_data'].get('item', {}).get('count', 0) if 'pokemon' in what: return pokecount if 'item' in what: return itemcount return '0' def get_player_info(self): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get('GET_INVENTORY', {}).get( 'inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: # print('item {}'.format(item)) playerdata = item.get('inventory_item_data', {}).get('player_stats') if playerdata: nextlvlxp = (int(playerdata.get('next_level_xp', 0)) - int(playerdata.get('experience', 0))) if 'level' in playerdata and 'experience' in playerdata: self.logger.info( 'Level: {level}'.format( **playerdata) + ' (Next Level: {} XP)'.format( nextlvlxp) + ' (Total: {experience} XP)' ''.format(**playerdata)) if 'pokemons_captured' in playerdata and 'poke_stop_visits' in playerdata: self.logger.info( 'Pokemon Captured: ' '{pokemons_captured}'.format( **playerdata) + ' | Pokestops Visited: ' '{poke_stop_visits}'.format( **playerdata)) def has_space_for_loot(self): number_of_things_gained_by_stop = 5 enough_space = ( self.get_inventory_count('item') < self._player['max_item_storage'] - number_of_things_gained_by_stop ) return enough_space def get_forts(self, order_by_distance=False): forts = [fort for fort in self.cell['forts'] if 'latitude' in fort and 'type' in fort] if order_by_distance: forts.sort(key=lambda x: distance( self.position[0], self.position[1], x['latitude'], x['longitude'] )) return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time() - self.last_time_map_object < self.config.map_object_cache_time: return self.last_map_object self.last_map_object = self.api.get_map_objects( latitude=f2i(lat), longitude=f2i(lng), since_timestamp_ms=timestamp, cell_id=cellid ) self.last_time_map_object = time.time() return self.last_map_object
class PokemonGoBot(object): @property def position(self): return self.api._position_lat, self.api._position_lng, 0 @position.setter def position(self, position_tuple): self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple @property def player_data(self): """ Returns the player data as received from the API. :return: The player data. :rtype: dict """ return self._player def __init__(self, config): self.config = config self.fort_timeouts = dict() self.pokemon_list = json.load( open(os.path.join('data', 'pokemon.json'))) self.item_list = json.load(open(os.path.join('data', 'items.json'))) self.metrics = Metrics(self) self.latest_inventory = None self.cell = None self.recent_forts = [None] * config.forts_max_circle_size self.tick_count = 0 self.softban = False self.start_position = None self.last_map_object = None self.last_time_map_object = 0 self.logger = logging.getLogger(type(self).__name__) # Make our own copy of the workers for this instance self.workers = [] # Theading setup for file writing self.web_update_queue = Queue.Queue(maxsize=1) self.web_update_thread = threading.Thread( target=self.update_web_location_worker) self.web_update_thread.start() def start(self): self._setup_event_system() self._setup_logging() self._setup_api() random.seed() def _setup_event_system(self): handlers = [LoggingHandler()] if self.config.websocket_server_url: if self.config.websocket_start_embedded_server: self.sio_runner = SocketIoRunner( self.config.websocket_server_url) self.sio_runner.start_listening_async() websocket_handler = SocketIoHandler( self, self.config.websocket_server_url) handlers.append(websocket_handler) if self.config.websocket_remote_control: remote_control = WebsocketRemoteControl(self).start() self.event_manager = EventManager(*handlers) self._register_events() if self.config.show_events: self.event_manager.event_report() sys.exit(1) # Registering event: # self.event_manager.register_event("location", parameters=['lat', 'lng']) # # Emitting event should be enough to add logging and send websocket # message: : # self.event_manager.emit('location', 'level'='info', data={'lat': 1, 'lng':1}), def _register_events(self): self.event_manager.register_event('location_found', parameters=('position', 'location')) self.event_manager.register_event('api_error') self.event_manager.register_event('config_error') self.event_manager.register_event('login_started') self.event_manager.register_event('login_failed') self.event_manager.register_event('login_successful') self.event_manager.register_event('set_start_location') self.event_manager.register_event('load_cached_location') self.event_manager.register_event('location_cache_ignored') self.event_manager.register_event( 'position_update', parameters=( 'current_position', 'last_position', 'distance', # optional 'distance_unit' # optional )) self.event_manager.register_event('location_cache_error') self.event_manager.register_event('bot_start') self.event_manager.register_event('bot_exit') # sleep stuff self.event_manager.register_event('next_sleep', parameters=('time', )) self.event_manager.register_event('bot_sleep', parameters=('time_in_seconds', )) # fort stuff self.event_manager.register_event('spun_fort', parameters=('fort_id', 'latitude', 'longitude')) self.event_manager.register_event('lured_pokemon_found', parameters=('fort_id', 'fort_name', 'encounter_id', 'latitude', 'longitude')) self.event_manager.register_event('moving_to_fort', parameters=('fort_name', 'distance')) self.event_manager.register_event('moving_to_lured_fort', parameters=('fort_name', 'distance', 'lure_distance')) self.event_manager.register_event('spun_pokestop', parameters=('pokestop', 'exp', 'items')) self.event_manager.register_event('pokestop_empty', parameters=('pokestop', )) self.event_manager.register_event('pokestop_out_of_range', parameters=('pokestop', )) self.event_manager.register_event('pokestop_on_cooldown', parameters=('pokestop', 'minutes_left')) self.event_manager.register_event('unknown_spin_result', parameters=('status_code', )) self.event_manager.register_event('pokestop_searching_too_often') self.event_manager.register_event('arrived_at_fort') # pokemon stuff self.event_manager.register_event( 'catchable_pokemon', parameters=('pokemon_id', 'spawn_point_id', 'encounter_id', 'latitude', 'longitude', 'expiration_timestamp_ms')) self.event_manager.register_event('pokemon_appeared', parameters=( 'pokemon', 'cp', 'iv', 'iv_display', )) self.event_manager.register_event('pokemon_catch_rate', parameters=('catch_rate', 'berry_name', 'berry_count')) self.event_manager.register_event('threw_berry', parameters=('berry_name', 'new_catch_rate')) self.event_manager.register_event('threw_pokeball', parameters=('pokeball', 'success_percentage', 'count_left')) self.event_manager.register_event('pokemon_escaped', parameters=('pokemon', )) self.event_manager.register_event('pokemon_vanished', parameters=('pokemon', )) self.event_manager.register_event('pokemon_caught', parameters=('pokemon', 'cp', 'iv', 'iv_display', 'exp')) self.event_manager.register_event('pokemon_evolved', parameters=('pokemon', 'iv', 'cp')) self.event_manager.register_event('pokemon_evolve_fail', parameters=('pokemon', )) self.event_manager.register_event('skip_evolve') self.event_manager.register_event('threw_berry_failed', parameters=('status_code', )) self.event_manager.register_event('vip_pokemon') # level up stuff self.event_manager.register_event('level_up', parameters=('previous_level', 'current_level')) self.event_manager.register_event('level_up_reward', parameters=('items', )) # lucky egg self.event_manager.register_event('used_lucky_egg', parameters=('amount_left', )) self.event_manager.register_event('lucky_egg_error') # softban self.event_manager.register_event('softban') self.event_manager.register_event('softban_fix') self.event_manager.register_event('softban_fix_done') # egg incubating self.event_manager.register_event('incubate_try', parameters=('incubator_id', 'egg_id')) self.event_manager.register_event('incubate', parameters=('distance_in_km', )) self.event_manager.register_event('next_egg_incubates', parameters=('distance_in_km', )) self.event_manager.register_event('incubator_already_used') self.event_manager.register_event('egg_already_incubating') self.event_manager.register_event('egg_hatched', parameters=('pokemon', 'cp', 'iv', 'exp', 'stardust', 'candy')) # discard item self.event_manager.register_event('item_discarded', parameters=('amount', 'item', 'maximum')) self.event_manager.register_event('item_discard_fail', parameters=('item', )) # inventory self.event_manager.register_event('inventory_full') # release self.event_manager.register_event('keep_best_release', parameters=('amount', 'pokemon', 'criteria')) self.event_manager.register_event('future_pokemon_release', parameters=('pokemon', 'cp', 'iv', 'below_iv', 'below_cp', 'cp_iv_logic')) self.event_manager.register_event('pokemon_release', parameters=('pokemon', 'cp', 'iv')) # polyline walker self.event_manager.register_event('polyline_request', parameters=('url', )) # cluster self.event_manager.register_event('found_cluster', parameters=('num_points', 'forts', 'radius', 'distance')) self.event_manager.register_event('arrived_at_cluster', parameters=('forts', 'radius')) # rename self.event_manager.register_event('rename_pokemon', parameters=('old_name', 'current_name')) self.event_manager.register_event('pokemon_nickname_invalid', parameters=('nickname', )) self.event_manager.register_event('unset_pokemon_nickname') # Move To map pokemon self.event_manager.register_event('move_to_map_pokemon_fail', parameters=('message', )) self.event_manager.register_event('move_to_map_pokemon_updated_map', parameters=('lat', 'lon')) self.event_manager.register_event('move_to_map_pokemon_teleport_to', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in')) self.event_manager.register_event('move_to_map_pokemon_encounter', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in')) self.event_manager.register_event('move_to_map_pokemon_move_towards', parameters=('poke_name', 'poke_dist', 'poke_lat', 'poke_lon', 'disappears_in')) self.event_manager.register_event('move_to_map_pokemon_teleport_back', parameters=('last_lat', 'last_lon')) def tick(self): self.health_record.heartbeat() self.cell = self.get_meta_cell() self.tick_count += 1 # Check if session token has expired self.check_session(self.position[0:2]) for worker in self.workers: if worker.work() == WorkerResult.RUNNING: return def get_meta_cell(self): location = self.position[0:2] cells = self.find_close_cells(*location) # Combine all cells into a single dict of the items we care about. forts = [] wild_pokemons = [] catchable_pokemons = [] for cell in cells: if "forts" in cell and len(cell["forts"]): forts += cell["forts"] if "wild_pokemons" in cell and len(cell["wild_pokemons"]): wild_pokemons += cell["wild_pokemons"] if "catchable_pokemons" in cell and len( cell["catchable_pokemons"]): catchable_pokemons += cell["catchable_pokemons"] # If there are forts present in the cells sent from the server or we don't yet have any cell data, return all data retrieved if len(forts) > 1 or not self.cell: return { "forts": forts, "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } # If there are no forts present in the data from the server, keep our existing fort data and only update the pokemon cells. else: return { "forts": self.cell["forts"], "wild_pokemons": wild_pokemons, "catchable_pokemons": catchable_pokemons } def update_web_location(self, cells=[], lat=None, lng=None, alt=None): # we can call the function with no arguments and still get the position # and map_cells if lat is None: lat = self.api._position_lat if lng is None: lng = self.api._position_lng if alt is None: alt = 0 if cells == []: location = self.position[0:2] cells = self.find_close_cells(*location) # insert detail info about gym to fort for cell in cells: if 'forts' in cell: for fort in cell['forts']: if fort.get('type') != 1: response_gym_details = self.api.get_gym_details( gym_id=fort.get('id'), player_latitude=lng, player_longitude=lat, gym_latitude=fort.get('latitude'), gym_longitude=fort.get('longitude')) fort['gym_details'] = response_gym_details.get( 'responses', {}).get('GET_GYM_DETAILS', None) user_data_cells = "data/cells-%s.json" % self.config.username with open(user_data_cells, 'w') as outfile: json.dump(cells, outfile) user_web_location = os.path.join( 'web', 'location-%s.json' % self.config.username) # alt is unused atm but makes using *location easier try: with open(user_web_location, 'w') as outfile: json.dump({ 'lat': lat, 'lng': lng, 'alt': alt, 'cells': cells }, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) user_data_lastlocation = os.path.join( 'data', 'last-location-%s.json' % self.config.username) try: with open(user_data_lastlocation, 'w') as outfile: json.dump( { 'lat': lat, 'lng': lng, 'start_position': self.start_position }, outfile) except IOError as e: self.logger.info('[x] Error while opening location file: %s' % e) def find_close_cells(self, lat, lng): cellid = get_cell_ids(lat, lng) timestamp = [ 0, ] * len(cellid) response_dict = self.get_map_objects(lat, lng, timestamp, cellid) map_objects = response_dict.get('responses', {}).get('GET_MAP_OBJECTS', {}) status = map_objects.get('status', None) map_cells = [] if status and status == 1: map_cells = map_objects['map_cells'] position = (lat, lng, 0) map_cells.sort( key=lambda x: distance(lat, lng, x['forts'][0]['latitude'], x[ 'forts'][0]['longitude']) if x.get('forts', []) else 1e6) return map_cells def _setup_logging(self): # log settings # log format if self.config.debug: log_level = logging.DEBUG logging.getLogger("requests").setLevel(logging.DEBUG) logging.getLogger("websocket").setLevel(logging.DEBUG) logging.getLogger("socketio").setLevel(logging.DEBUG) logging.getLogger("engineio").setLevel(logging.DEBUG) logging.getLogger("socketIO-client").setLevel(logging.DEBUG) logging.getLogger("pgoapi").setLevel(logging.DEBUG) logging.getLogger("rpc_api").setLevel(logging.DEBUG) else: log_level = logging.ERROR logging.getLogger("requests").setLevel(logging.ERROR) logging.getLogger("websocket").setLevel(logging.ERROR) logging.getLogger("socketio").setLevel(logging.ERROR) logging.getLogger("engineio").setLevel(logging.ERROR) logging.getLogger("socketIO-client").setLevel(logging.ERROR) logging.getLogger("pgoapi").setLevel(logging.ERROR) logging.getLogger("rpc_api").setLevel(logging.ERROR) logging.basicConfig( level=log_level, format='%(asctime)s [%(name)10s] [%(levelname)s] %(message)s') def check_session(self, position): # Check session expiry if self.api._auth_provider and self.api._auth_provider._ticket_expire: # prevent crash if return not numeric value if not self.is_numeric(self.api._auth_provider._ticket_expire): self.logger.info("Ticket expired value is not numeric", 'yellow') return remaining_time = \ self.api._auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.event_manager.emit( 'api_error', sender=self, level='info', formatted='Session stale, re-logging in.') position = self.position self.api = ApiWrapper() self.position = position self.login() self.api.activate_signature(self.get_encryption_lib()) @staticmethod def is_numeric(s): try: float(s) return True except ValueError: return False def login(self): self.event_manager.emit('login_started', sender=self, level='info', formatted="Login procedure started.") lat, lng = self.position[0:2] self.api.set_position(lat, lng, 0) while not self.api.login(self.config.auth_service, str(self.config.username), str(self.config.password)): self.event_manager.emit( 'login_failed', sender=self, level='info', formatted= "Login error, server busy. Waiting 10 seconds to try again.") time.sleep(10) self.event_manager.emit('login_successful', sender=self, level='info', formatted="Login successful.") def get_encryption_lib(self): if _platform == "linux" or _platform == "linux2" or _platform == "darwin": file_name = 'encrypt.so' elif _platform == "Windows" or _platform == "win32": # Check if we are on 32 or 64 bit if sys.maxsize > 2**32: file_name = 'encrypt_64.dll' else: file_name = 'encrypt.dll' if self.config.encrypt_location == '': path = os.path.abspath( os.path.join(os.path.dirname(__file__), os.pardir)) else: path = self.config.encrypt_location full_path = path + '/' + file_name if not os.path.isfile(full_path): self.logger.error( file_name + ' is not found! Please place it in the bots root directory or set libencrypt_location in config.' ) self.logger.info('Platform: ' + _platform + ' Encrypt.so directory: ' + path) sys.exit(1) else: self.logger.info('Found ' + file_name + '! Platform: ' + _platform + ' Encrypt.so directory: ' + path) return full_path def _setup_api(self): # instantiate pgoapi self.api = ApiWrapper() # provide player position on the earth self._set_starting_position() self.login() # chain subrequests (methods) into one RPC call self._print_character_info() self.api.activate_signature(self.get_encryption_lib()) self.logger.info('') self.update_inventory() # send empty map_cells and then our position self.update_web_location() def _print_character_info(self): # get player profile call # ---------------------- response_dict = self.api.get_player() # print('Response dictionary: \n\r{}'.format(json.dumps(response_dict, indent=2))) currency_1 = "0" currency_2 = "0" if response_dict: self._player = response_dict['responses']['GET_PLAYER'][ 'player_data'] player = self._player else: self.logger.info( "The API didn't return player info, servers are unstable - " "retrying.", 'red') sleep(5) self._print_character_info() # @@@ TODO: Convert this to d/m/Y H:M:S creation_date = datetime.datetime.fromtimestamp( player['creation_timestamp_ms'] / 1e3) creation_date = creation_date.strftime("%Y/%m/%d %H:%M:%S") pokecoins = '0' stardust = '0' items_stock = self.current_inventory() if 'amount' in player['currencies'][0]: pokecoins = player['currencies'][0]['amount'] if 'amount' in player['currencies'][1]: stardust = player['currencies'][1]['amount'] self.logger.info('') self.logger.info('--- {username} ---'.format(**player)) self.get_player_info() self.logger.info('Pokemon Bag: {}/{}'.format( self.get_inventory_count('pokemon'), player['max_pokemon_storage'])) self.logger.info('Items: {}/{}'.format( self.get_inventory_count('item'), player['max_item_storage'])) self.logger.info('Stardust: {}'.format(stardust) + ' | Pokecoins: {}'.format(pokecoins)) # Items Output self.logger.info('PokeBalls: ' + str(items_stock[1]) + ' | GreatBalls: ' + str(items_stock[2]) + ' | UltraBalls: ' + str(items_stock[3])) self.logger.info('RazzBerries: ' + str(items_stock[701]) + ' | BlukBerries: ' + str(items_stock[702]) + ' | NanabBerries: ' + str(items_stock[703])) self.logger.info('LuckyEgg: ' + str(items_stock[301]) + ' | Incubator: ' + str(items_stock[902]) + ' | TroyDisk: ' + str(items_stock[501])) self.logger.info('Potion: ' + str(items_stock[101]) + ' | SuperPotion: ' + str(items_stock[102]) + ' | HyperPotion: ' + str(items_stock[103]) + ' | MaxPotion: ' + str(items_stock[104])) self.logger.info('Incense: ' + str(items_stock[401]) + ' | IncenseSpicy: ' + str(items_stock[402]) + ' | IncenseCool: ' + str(items_stock[403])) self.logger.info('Revive: ' + str(items_stock[201]) + ' | MaxRevive: ' + str(items_stock[202])) self.logger.info('') def use_lucky_egg(self): return self.api.use_item_xp_boost(item_id=301) def get_inventory(self): if self.latest_inventory is None: self.latest_inventory = self.api.get_inventory() return self.latest_inventory def update_inventory(self): response = self.get_inventory() self.inventory = list() inventory_items = response.get('responses', {}).get( 'GET_INVENTORY', {}).get('inventory_delta', {}).get('inventory_items', {}) if inventory_items: for item in inventory_items: item_info = item.get('inventory_item_data', {}).get('item', {}) if {"item_id", "count"}.issubset(set(item_info.keys())): self.inventory.append(item['inventory_item_data']['item']) def current_inventory(self): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items'] user_web_inventory = 'web/inventory-%s.json' % self.config.username with open(user_web_inventory, 'w') as outfile: json.dump(inventory_dict, outfile) # get player items stock # ---------------------- items_stock = {x.value: 0 for x in list(Item)} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_count = item_dict.get('count') item_id = item_dict.get('item_id') if item_count and item_id: if item_id in items_stock: items_stock[item_id] = item_count return items_stock def item_inventory_count(self, id): inventory_req = self.get_inventory() inventory_dict = inventory_req['responses']['GET_INVENTORY'][ 'inventory_delta']['inventory_items'] if id == 'all': return self._all_items_inventory_count(inventory_dict) else: return self._item_inventory_count_per_id(id, inventory_dict) def _item_inventory_count_per_id(self, id, inventory_dict): item_count = 0 for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id == int(id) and item_count: return item_count return 0 def _all_items_inventory_count(self, inventory_dict): item_count_dict = {} for item in inventory_dict: item_dict = item.get('inventory_item_data', {}).get('item', {}) item_id = item_dict.get('item_id', False) item_count = item_dict.get('count', False) if item_id and item_count: item_count_dict[item_id] = item_count return item_count_dict def _set_starting_position(self): self.event_manager.emit('set_start_location', sender=self, level='info', formatted='Setting start location.') has_position = False if self.config.test: # TODO: Add unit tests return if self.config.location: location_str = self.config.location location = self.get_pos_by_name(location_str.replace(" ", "")) msg = "Location found: {location} {position}" self.event_manager.emit('location_found', sender=self, level='info', formatted=msg, data={ 'location': location_str, 'position': location }) self.api.set_position(*location) self.event_manager.emit('position_update', sender=self, level='info', formatted="Now at {current_position}", data={ 'current_position': self.position, 'last_position': '', 'distance': '', 'distance_unit': '' }) self.start_position = self.position has_position = True if self.config.location_cache: try: # save location flag used to pull the last known location from # the location.json self.event_manager.emit('load_cached_location', sender=self, level='debug', formatted='Loading cached location...') with open('data/last-location-%s.json' % self.config.username) as f: location_json = json.load(f) location = (location_json['lat'], location_json['lng'], 0.0) # If location has been set in config, only use cache if starting position has not differed if has_position and 'start_position' in location_json: last_start_position = tuple( location_json.get('start_position', [])) # Start position has to have been set on a previous run to do this check if last_start_position and last_start_position != self.start_position: msg = 'Going to a new place, ignoring cached location.' self.event_manager.emit('location_cache_ignored', sender=self, level='debug', formatted=msg) return self.api.set_position(*location) self.event_manager.emit( 'position_update', sender=self, level='debug', formatted='Loaded location {current_position} from cache', data={ 'current_position': location, 'last_position': '', 'distance': '', 'distance_unit': '' }) has_position = True except Exception: if has_position is False: sys.exit( "No cached Location. Please specify initial location.") self.event_manager.emit( 'location_cache_error', sender=self, level='debug', formatted='Parsing cached location failed.') def get_pos_by_name(self, location_name): # Check if the given location is already a coordinate. if ',' in location_name: possible_coordinates = re.findall("[-]?\d{1,3}[.]\d{3,7}", location_name) if len(possible_coordinates) == 2: # 2 matches, this must be a coordinate. We'll bypass the Google # geocode so we keep the exact location. self.logger.info( '[x] Coordinates found in passed in location, ' 'not geocoding.') return float(possible_coordinates[0]), float( possible_coordinates[1]), float("0.0") geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) return float(loc.latitude), float(loc.longitude), float(loc.altitude) def heartbeat(self): # Remove forts that we can now spin again. self.fort_timeouts = { id: timeout for id, timeout in self.fort_timeouts.iteritems() if timeout >= time.time() * 1000 } request = self.api.create_request() request.get_player() request.check_awarded_badges() request.call() try: self.web_update_queue.put_nowait( True) # do this outside of thread every tick except Queue.Full: pass def update_web_location_worker(self): while True: self.web_update_queue.get() self.update_web_location() def get_inventory_count(self, what): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get( 'GET_INVENTORY', {}).get('inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: if 'inventory_item_data' in item: if 'pokemon_data' in item['inventory_item_data']: pokecount += 1 itemcount += item['inventory_item_data'].get( 'item', {}).get('count', 0) if 'pokemon' in what: return pokecount if 'item' in what: return itemcount return '0' def get_player_info(self): response_dict = self.get_inventory() inventory_items = response_dict.get('responses', {}).get( 'GET_INVENTORY', {}).get('inventory_delta', {}).get('inventory_items', {}) if inventory_items: pokecount = 0 itemcount = 1 for item in inventory_items: # print('item {}'.format(item)) playerdata = item.get('inventory_item_data', {}).get('player_stats') if playerdata: nextlvlxp = (int(playerdata.get('next_level_xp', 0)) - int(playerdata.get('experience', 0))) if 'level' in playerdata and 'experience' in playerdata: self.logger.info( 'Level: {level}'.format(**playerdata) + ' (Next Level: {} XP)'.format(nextlvlxp) + ' (Total: {experience} XP)' ''.format(**playerdata)) if 'pokemons_captured' in playerdata and 'poke_stop_visits' in playerdata: self.logger.info( 'Pokemon Captured: ' '{pokemons_captured}'.format(**playerdata) + ' | Pokestops Visited: ' '{poke_stop_visits}'.format(**playerdata)) def has_space_for_loot(self): number_of_things_gained_by_stop = 5 enough_space = ( self.get_inventory_count('item') < self._player['max_item_storage'] - number_of_things_gained_by_stop) return enough_space def get_forts(self, order_by_distance=False): forts = [ fort for fort in self.cell['forts'] if 'latitude' in fort and 'type' in fort ] if order_by_distance: forts.sort(key=lambda x: distance(self.position[0], self.position[ 1], x['latitude'], x['longitude'])) return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time( ) - self.last_time_map_object < self.config.map_object_cache_time: return self.last_map_object self.last_map_object = self.api.get_map_objects( latitude=f2i(lat), longitude=f2i(lng), since_timestamp_ms=timestamp, cell_id=cellid) self.last_time_map_object = time.time() return self.last_map_object