Beispiel #1
0
class Lock:
    """ UNIX-specific exclusive file locks (released when the process ends).

    Based on
    http://blog.vmfarms.com/2011/03/cross-process-locking-and.html,
    adapted for context managers (the 'with' statement).

    Modified to be gevent-safe! Locks held by a given Greenlet may not be
    taken by other Greenlets until released, _as long as you only create one
    Lock object per lockfile_. THIS IS VERY IMPORTANT. *Make sure* that you're
    not creating multiple locks on the same file from the same process,
    otherwise you'll bypass the gevent lock!

    Parameters
    ----------
    f : file or str
        File handle or filename to use as the lock.
    block : bool
        Whether to block or throw IOError if the lock is grabbed multiple
        times.
    """
    TIMEOUT = 60

    def __init__(self, f, block=True):
        if isinstance(f, file):
            self.filename = f.name
            self.handle = f if not f.closed else open(f, 'w')
        else:
            self.filename = f
            mkdirp(os.path.dirname(f))
            self.handle = open(f, 'w')
        if block:
            self.lock_op = fcntl.LOCK_EX
        else:
            self.lock_op = fcntl.LOCK_EX | fcntl.LOCK_NB
        self.block = block
        self.gevent_lock = BoundedSemaphore(1)

    def acquire(self):
        got_gevent_lock = self.gevent_lock.acquire(blocking=self.block)
        if not got_gevent_lock:
            raise IOError("cannot acquire gevent lock")
        fcntl.flock(self.handle, self.lock_op)

    def release(self):
        fcntl.flock(self.handle, fcntl.LOCK_UN)
        self.gevent_lock.release()

    def locked(self):
        return self.gevent_lock.locked()

    def __enter__(self):
        self.acquire()
        return self

    def __exit__(self, type, value, traceback):
        self.release()

    def __del__(self):
        self.handle.close()
Beispiel #2
0
class Poketrainer:
    """ Public functions (without _**) are callable by the webservice! """

    def __init__(self, args):

        self.thread = None
        self.socket = None
        self.cli_args = args
        self.force_debug = args['debug']

        self.log = logging.getLogger(__name__)

        # timers, counters and triggers
        self.pokemon_caught = 0
        self._error_counter = 0
        self._error_threshold = 10
        self.start_time = time()
        self.exp_start = None
        self._heartbeat_number = 1  # setting this back to one because we make parse a full heartbeat during login!
        self._heartbeat_frequency = 3  # 1 = always
        self._full_heartbeat_frequency = 15  # 10 = as before (every 10th heartbeat)
        self._farm_mode_triggered = False

        # objects, order is important!
        self.config = None
        self._load_config()
        self._open_socket()

        self.player = Player({})
        self.player_stats = PlayerStats({})
        self.inventory = Inventory(self, [])
        self.fort_walker = FortWalker(self)
        self.map_objects = MapObjects(self)
        self.poke_catcher = PokeCatcher(self)
        self.incubate = Incubate(self)
        self.evolve = Evolve(self)
        self.release = Release(self)
        self.sniper = Sniper(self)

        self._origPosF = (0, 0, 0)
        self.api = None
        self._load_api()

        # config values that might be changed during runtime
        self.step_size = self.config.step_size
        self.should_catch_pokemon = self.config.should_catch_pokemon

        # threading / locking
        self.sem = BoundedSemaphore(1)  # gevent
        self.persist_lock = False
        self.locker = None

    def sleep(self, t):
        # eventlet.sleep(t * self.config.sleep_mult)
        gevent.sleep(t * self.config.sleep_mult)

    def _open_socket(self):
        desc_file = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), ".listeners")
        s = socket.socket()
        s.bind(("", 0))  # let the kernel find a free port
        sock_port = s.getsockname()[1]
        s.close()
        data = {}

        if os.path.isfile(desc_file):
            with open(desc_file, 'r+') as f:
                data = f.read()
                if PY2:
                    data = json.loads(data.encode() if len(data) > 0 else '{}')
                else:
                    data = json.loads(data if len(data) > 0 else '{}')
        data[self.config.username] = sock_port
        with open(desc_file, "w+") as f:
            f.write(json.dumps(data, indent=2))

        s = zerorpc.Server(self)
        s.bind("tcp://127.0.0.1:%i" % sock_port)  # the free port should still be the same
        self.socket = gevent.spawn(s.run)

        # zerorpc requires gevent, thus we would need a solution for eventlets
        # self.socket = self.thread_pool.spawn(wsgi.server, eventlet.listen(('127.0.0.1', sock_port)), self)
        # self.socket = self.thread_pool.spawn(eventlet.serve, eventlet.listen(('127.0.0.1', sock_port)), self)
        # alternative: GreenRPCService

    def _load_config(self):
        if self.config is None:
            config_file = "config.json"

            # If config file exists, load variables from json
            load = {}
            if os.path.isfile(config_file):
                with open(config_file) as data:
                    load.update(json.load(data))

            defaults = load.get('defaults', {})
            config = load.get('accounts', [])[self.cli_args['config_index']]

            if self.cli_args['debug'] or config.get('debug', False):
                logging.getLogger("requests").setLevel(logging.DEBUG)
                logging.getLogger("pgoapi").setLevel(logging.DEBUG)
                logging.getLogger("poketrainer").setLevel(logging.DEBUG)
                logging.getLogger("rpc_api").setLevel(logging.DEBUG)

            if config.get('auth_service', '') not in ['ptc', 'google']:
                logger.error("Invalid Auth service specified for account %s! ('ptc' or 'google')", config.get('username', 'NA'))
                return False

                # merge account section with defaults
            self.config = Config(dict_merge(defaults, config), self.cli_args)
        return True

    def reload_config(self):
        self.config = None
        return self._load_config()

    def _load_api(self, prev_location=None):
        if self.api is None:
            self.api = api.pgoapi.PGoApi()
            # set signature!
            self.api.activate_signature(
                os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), self.cli_args['encrypt_lib'])
            )

            # get position and set it in the API
            if self.cli_args['location']:
                position = get_location(self.cli_args['location'])
            else:
                position = get_location(self.config.location)
            self._origPosF = position
            if prev_location:
                position = prev_location
            self.api.set_position(*position)

            # retry login every 30 seconds if any errors
            self.log.info('Starting Login process...')
            login = False
            while not login:
                login = self.api.login(self.config.auth_service, self.config.username, self.config.get_password())
                if not login:
                    logger.error('Login error, retrying Login in 30 seconds')
                    self.sleep(30)
            self.log.info('Login successful')
            self._heartbeat(login, True)
        return True

    def reload_api(self, prev_location=None):
        self.api = None
        return self._load_api(prev_location)

    '''
    Blocking lock
        - only locks if current thread (greenlet) doesn't own the lock
        - persist=True will ensure the lock will not be released until the user
          explicitly sets self.persist_lock=False.
    '''
    def thread_lock(self, persist=False):
        if self.sem.locked():
            if self.locker == id(gevent.getcurrent()):
                self.log.debug("Locker is -- %s. No need to re-lock", id(gevent.getcurrent()))
                return False
            else:
                self.log.debug("Already locked by %s. Greenlet %s will wait...", self.locker, id(gevent.getcurrent()))
        self.sem.acquire()
        self.persist_lock = persist
        self.locker = id(gevent.getcurrent())
        self.log.debug("%s acquired lock (persist=%s)!", self.locker, persist)
        return True

    '''
    Releases the lock if needed and the user didn't persist it
    '''
    def thread_release(self):
        if self.sem.locked() and self.locker == id(gevent.getcurrent()) and not self.persist_lock:
            self.log.debug("%s is now releasing lock", id(gevent.getcurrent()))
            self.sem.release()

    def _callback(self, gt):
        try:
            if not gt.exception:
                result = gt.value
                logger.info('Thread finished with result: %s', result)
        except KeyboardInterrupt:
            return

        logger.exception('Error in main loop %s, restarting at location: %s',
                         gt.exception, self.get_position())
        # restart after sleep
        self.sleep(30)
        self.reload_config()
        self.reload_api(self.get_position())
        self.start()

    def start(self):
        self.thread = gevent.spawn(self._main_loop)

        self.thread.link(self._callback)

    def stop(self):
        if self.thread:
            self.thread.kill()

    def _main_loop(self):
        if self.config.enable_caching and self.config.experimental:
            if not self.config.use_cache:
                self.log.info('==== CACHING MODE: CACHE FORTS ====')
            else:
                self.log.info('==== CACHING MODE: ROUTE+SPIN CACHED FORTS ====')
            self.fort_walker.setup_cache()
        while True:
            # acquire lock for this thread
            if self.thread_lock(persist=True):
                try:
                    self._heartbeat()

                    self.poke_catcher.catch_all()
                    self.fort_walker.loop()
                    self.fort_walker.spin_nearest_fort()
                finally:
                    # after we're done, release lock
                    self.persist_lock = False
                    self.thread_release()
            self.sleep(1.0)

    def _heartbeat(self, res=False, login_response=False):
        if not isinstance(res, dict):
            # limit the amount of heartbeats, every second is just too much in my opinion!
            if (not self._heartbeat_number % self._heartbeat_frequency == 0 and
                    not self._heartbeat_number % self._full_heartbeat_frequency == 0):
                self._heartbeat_number += 1
                return

            # making a standard call to update position, etc
            req = self.api.create_request()
            req.get_player()
            if self._heartbeat_number % 10 == 0:
                req.check_awarded_badges()
                req.get_inventory()
            res = req.call()
            if not res or res.get("direction", -1) == 102:
                self.log.error("There were a problem responses for api call: %s. Restarting!!!", res)
                self.api.force_refresh_access_token()
                raise AuthException("Token probably expired?")

        self.log.debug(
            'Response dictionary: \n\r{}'.format(json.dumps(res, indent=2, default=lambda obj: obj.decode('utf8'))))

        responses = res.get('responses', {})
        if 'GET_PLAYER' in responses:
            self.player = Player(responses.get('GET_PLAYER', {}).get('player_data', {}))
            self.log.info("Player Info: {0}, Pokemon Caught in this run: {1}".format(self.player, self.pokemon_caught))

        if 'GET_INVENTORY' in responses:

            # update objects
            inventory_items = responses.get('GET_INVENTORY', {}).get('inventory_delta', {}).get('inventory_items', [])
            self.inventory = Inventory(self, inventory_items)
            for inventory_item in self.inventory.inventory_items:
                if "player_stats" in inventory_item['inventory_item_data']:
                    self.player_stats = PlayerStats(
                        inventory_item['inventory_item_data']['player_stats'],
                        self.pokemon_caught, self.start_time, self.exp_start
                    )
                    if self.exp_start is None:
                        self.exp_start = self.player_stats.run_exp_start
                    self.log.info("Player Stats: {}".format(self.player_stats))
            if self.config.list_inventory_before_cleanup:
                self.log.info("Player Inventory: %s", self.inventory)
            if not login_response:
                self.log.debug(self.inventory.cleanup_inventory())
                self.log.info("Player Inventory after cleanup: %s", self.inventory)
            if self.config.list_pokemon_before_cleanup:
                self.log.info(os.linesep.join(map(str, self.inventory.get_caught_pokemon())))

            if not login_response:
                # maintenance
                self.incubate.incubate_eggs()
                self.inventory.use_lucky_egg()
                self.evolve.attempt_evolve()
                self.release.cleanup_pokemon()

            # save data dump
            with open("data_dumps/%s.json" % self.config.username, "w") as f:
                posf = self.get_position()
                responses['lat'] = posf[0]
                responses['lng'] = posf[1]
                responses['GET_PLAYER']['player_data']['hourly_exp'] = self.player_stats.run_hourly_exp
                f.write(json.dumps(responses, indent=2, default=lambda obj: obj.decode('utf8')))

            # Farm precon
            if self.config.farm_items_enabled:
                pokeball_count = 0
                if not self.config.farm_ignore_pokeball_count:
                    pokeball_count += self.inventory.poke_balls
                if not self.config.farm_ignore_greatball_count:
                    pokeball_count += self.inventory.great_balls
                if not self.config.farm_ignore_ultraball_count:
                    pokeball_count += self.inventory.ultra_balls
                if not self.config.farm_ignore_masterball_count:
                    pokeball_count += self.inventory.master_balls
                if self.config.pokeball_farm_threshold > pokeball_count and not self._farm_mode_triggered:
                    self.should_catch_pokemon = False
                    self._farm_mode_triggered = True
                    self.log.info("Player only has %s Pokeballs, farming for more...", pokeball_count)
                    if self.config.farm_override_step_size != -1:
                        self.step_size = self.config.farm_override_step_size
                        self.log.info("Player has changed speed to %s", self.step_size)
                elif self.config.pokeball_continue_threshold <= pokeball_count and self._farm_mode_triggered:
                    self.should_catch_pokemon = self.config.should_catch_pokemon  # Restore catch pokemon setting from config file
                    self._farm_mode_triggered = False
                    self.log.info("Player has %s Pokeballs, continuing to catch more!", pokeball_count)
                    if self.config.farm_override_step_size != -1:
                        self.step_size = self.config.step_size
                        self.log.info("Player has returned to normal speed of %s", self.step_size)

        if 'DOWNLOAD_SETTINGS' in responses:
            settings = responses.get('DOWNLOAD_SETTINGS', {}).get('settings', {})
            if settings.get('minimum_client_version', '0.0.0') > '0.33.0':
                self.log.error("Minimum client version has changed... the bot needs to be updated! Will now stop!")
                exit(0)
            map_settings = settings.get('map_settings', {})

            get_map_objects_min_refresh_seconds = map_settings.get('get_map_objects_min_refresh_seconds', 0.0)  # std. 5.0
            if get_map_objects_min_refresh_seconds != self.map_objects.get_api_rate_limit():
                self.map_objects.update_rate_limit(get_map_objects_min_refresh_seconds)

            """
            fort_settings = settings.get('fort_settings', {})
            inventory_settings = settings.get('inventory_settings', {})

            get_map_objects_max_refresh_seconds = map_settings.get('get_map_objects_max_refresh_seconds', 30.0)
            get_map_objects_min_distance_meters = map_settings.get('get_map_objects_min_distance_meters', 10.0)
            encounter_range_meters = map_settings.get('encounter_range_meters', 50.0)
            poke_nav_range_meters = map_settings.get('poke_nav_range_meters', 201.0)
            pokemon_visible_range = map_settings.get('pokemon_visible_range', 70.0)
            get_map_objects_min_refresh_seconds = map_settings.get('get_map_objects_min_refresh_seconds', 5.0)
            google_maps_api_key = map_settings.get('google_maps_api_key', '')

            self.log.info('complete settings: %s', responses.get('DOWNLOAD_SETTINGS', {}))

            self.log.info('minimum_client_version: %s', str(settings.get('minimum_client_version', '0.0.0')))

            self.log.info('poke_nav_range_meters: %s', str(poke_nav_range_meters))
            self.log.info('pokemon_visible_range: %s', str(pokemon_visible_range))

            self.log.info('get_map_objects_min_refresh_seconds: %s', str(get_map_objects_min_refresh_seconds))
            self.log.info('get_map_objects_max_refresh_seconds: %s', str(get_map_objects_max_refresh_seconds))
            self.log.info('get_map_objects_min_distance_meters: %s', str(get_map_objects_min_distance_meters))
            self.log.info('encounter_range_meters: %s', str(encounter_range_meters))
            """

        self._heartbeat_number += 1
        return res

    def set_position(self, *pos):
        return self.api.set_position(*pos)

    def get_position(self):
        return self.api.get_position()

    def get_orig_position(self):
        return self._origPosF

    """ FOLLOWING ARE FUNCTIONS FOR THE WEB LISTENER """

    def release_pokemon_by_id(self, p_id):
        # acquire lock for this thread
        if self.thread_lock(persist=True):
            try:
                return self.release.do_release_pokemon_by_id(p_id)
            finally:
                # after we're done, release lock
                self.persist_lock = False
                self.thread_release()
        else:
            return 'Only one Simultaneous request allowed'

    def current_location(self):
        self.log.info("Web got position: %s", self.get_position())
        return self.get_position()

    def get_caught_pokemons(self):
        return self.inventory.get_caught_pokemon_by_family(as_json=True)

    def get_inventory(self):
        return self.inventory.to_json()

    def get_player_info(self):
        return self.player.to_json()

    def snipe_pokemon(self, lat, lng):
        # acquire lock for this thread
        if self.thread_lock(persist=True):
            try:
                return self.sniper.snipe_pokemon(float(lat), float(lng))
            finally:
                # after we're done, release lock
                self.map_objects.wait_for_api_timer()
                self.persist_lock = False
                self.thread_release()
        else:
            return 'Only one Simultaneous request allowed'

    def ping(self):
        self.log.info("Responding to ping")
        return "pong"
Beispiel #3
0
class Poketrainer(object):
    """ Public functions (without _**) are callable by the webservice! """
    def __init__(self, args):

        self.thread = None
        self.socket = None
        self.cli_args = args
        self.force_debug = args['debug']

        # timers, counters and triggers
        self.pokemon_caught = 0
        self._error_counter = 0
        self._error_threshold = 10
        self.start_time = time()
        self.exp_start = None
        self._heartbeat_number = 1  # setting this back to one because we make parse a full heartbeat during login!
        self._heartbeat_frequency = 3  # 1 = always
        self._full_heartbeat_frequency = 15  # 10 = as before (every 10th heartbeat)
        self._farm_mode_triggered = False

        # objects, order is important!
        self.config = None
        self._load_config()

        self.log = create_logger(__name__,
                                 self.config.log_colors["poketrainer".upper()])

        self._open_socket()

        self.player = Player({})
        self.player_stats = PlayerStats({})
        self.inventory = Inventory(self, [])
        self.fort_walker = FortWalker(self)
        self.map_objects = MapObjects(self)
        self.poke_catcher = PokeCatcher(self)
        self.incubate = Incubate(self)
        self.evolve = Evolve(self)
        self.release = Release(self)
        self.sniper = Sniper(self)

        self._origPosF = (0, 0, 0)
        self.api = None
        self._load_api()

        # config values that might be changed during runtime
        self.step_size = self.config.step_size
        self.should_catch_pokemon = self.config.should_catch_pokemon

        # threading / locking
        self.sem = BoundedSemaphore(1)  # gevent
        self.persist_lock = False
        self.locker = None

    def sleep(self, t):
        # eventlet.sleep(t * self.config.sleep_mult)
        gevent.sleep(t * self.config.sleep_mult)

    def _open_socket(self):
        desc_file = os.path.join(
            os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
            ".listeners")
        s = socket.socket()
        s.bind(("", 0))  # let the kernel find a free port
        sock_port = s.getsockname()[1]
        s.close()
        data = {}

        if os.path.isfile(desc_file):
            with open(desc_file, 'r+') as f:
                data = f.read()
                if PY2:
                    data = json.loads(data.encode() if len(data) > 0 else '{}')
                else:
                    data = json.loads(data if len(data) > 0 else '{}')
        data[self.config.username] = sock_port
        with open(desc_file, "w+") as f:
            f.write(json.dumps(data, indent=2))

        s = zerorpc.Server(self)
        s.bind("tcp://127.0.0.1:%i" %
               sock_port)  # the free port should still be the same
        self.socket = gevent.spawn(s.run)

        # zerorpc requires gevent, thus we would need a solution for eventlets
        # self.socket = self.thread_pool.spawn(wsgi.server, eventlet.listen(('127.0.0.1', sock_port)), self)
        # self.socket = self.thread_pool.spawn(eventlet.serve, eventlet.listen(('127.0.0.1', sock_port)), self)
        # alternative: GreenRPCService

    def _load_config(self):
        if self.config is None:
            config_file = "config.json"

            # If config file exists, load variables from json
            load = {}
            if os.path.isfile(config_file):
                with open(config_file) as data:
                    load.update(json.load(data))

            defaults = load.get('defaults', {})
            config = load.get('accounts', [])[self.cli_args['config_index']]

            if self.cli_args['debug'] or config.get('debug', False):
                colorlog.getLogger("requests").setLevel(logging.DEBUG)
                colorlog.getLogger("pgoapi").setLevel(logging.DEBUG)
                colorlog.getLogger("poketrainer").setLevel(logging.DEBUG)
                colorlog.getLogger("rpc_api").setLevel(logging.DEBUG)

            if config.get('auth_service', '') not in ['ptc', 'google']:
                self.log.error(
                    "Invalid Auth service specified for account %s! ('ptc' or 'google')",
                    config.get('username', 'NA'))
                return False

                # merge account section with defaults
            self.config = Config(dict_merge(defaults, config), self.cli_args)
        return True

    def reload_config(self):
        self.config = None
        return self._load_config()

    def _load_api(self, prev_location=None):
        if self.api is None:
            self.api = api.pgoapi.PGoApi()
            # set signature!
            self.api.activate_signature(
                os.path.join(
                    os.path.dirname(os.path.dirname(
                        os.path.realpath(__file__))),
                    self.cli_args['encrypt_lib']))

            # get position and set it in the API
            if self.cli_args['location']:
                position = get_location(self.cli_args['location'])
            else:
                position = get_location(self.config.location)
            self._origPosF = position
            if prev_location:
                position = prev_location
            self.api.set_position(*position)

            # retry login every 30 seconds if any errors
            self.log.info('Starting Login process...')
            login = False
            while not login:
                login = self.api.login(self.config.auth_service,
                                       self.config.username,
                                       self.config.get_password())
                if not login:
                    self.log.error('Login error, retrying Login in 30 seconds')
                    self.sleep(30)
            self.log.info('Login successful')
            self._heartbeat(login, True)
        return True

    def reload_api(self, prev_location=None):
        self.api = None
        return self._load_api(prev_location)

    '''
    Blocking lock
        - only locks if current thread (greenlet) doesn't own the lock
        - persist=True will ensure the lock will not be released until the user
          explicitly sets self.persist_lock=False.
    '''

    def thread_lock(self, persist=False):
        if self.sem.locked():
            if self.locker == id(gevent.getcurrent()):
                self.log.debug("Locker is -- %s. No need to re-lock",
                               id(gevent.getcurrent()))
                return False
            else:
                self.log.debug(
                    "Already locked by %s. Greenlet %s will wait...",
                    self.locker, id(gevent.getcurrent()))
        self.sem.acquire()
        self.persist_lock = persist
        self.locker = id(gevent.getcurrent())
        self.log.debug("%s acquired lock (persist=%s)!", self.locker, persist)
        return True

    '''
    Releases the lock if needed and the user didn't persist it
    '''

    def thread_release(self):
        if self.sem.locked() and self.locker == id(
                gevent.getcurrent()) and not self.persist_lock:
            self.log.debug("%s is now releasing lock", id(gevent.getcurrent()))
            self.sem.release()

    def _callback(self, gt):
        try:
            if not gt.exception:
                result = gt.value
                self.log.info('Thread finished with result: %s', result)
        except KeyboardInterrupt:
            return

        self.log.exception('Error in main loop %s, restarting at location: %s',
                           gt.exception, self.get_position())
        # restart after sleep
        self.sleep(30)
        self.reload_config()
        self.reload_api(self.get_position())
        self.start()

    def start(self):
        self.thread = gevent.spawn(self._main_loop)

        self.thread.link(self._callback)

    def stop(self):
        if self.thread:
            self.thread.kill()

    def _main_loop(self):
        if self.config.enable_caching and self.config.experimental:
            if not self.config.use_cache:
                self.log.info('==== CACHING MODE: CACHE FORTS ====')
            else:
                self.log.info(
                    '==== CACHING MODE: ROUTE+SPIN CACHED FORTS ====')
            self.fort_walker.setup_cache()
        while True:
            # acquire lock for this thread
            if self.thread_lock(persist=True):
                try:
                    self._heartbeat()
                    self.fort_walker.loop()
                    self.fort_walker.spin_nearest_fort()
                    self.poke_catcher.catch_all()

                finally:
                    # after we're done, release lock
                    self.persist_lock = False
                    self.thread_release()
            # self.log.info("COMPLETED A _main_loop")
            self.sleep(1.0)

    def _heartbeat(self, res=False, login_response=False):
        if not isinstance(res, dict):
            # limit the amount of heartbeats, every second is just too much in my opinion!
            if (not self._heartbeat_number % self._heartbeat_frequency == 0
                    and not self._heartbeat_number %
                    self._full_heartbeat_frequency == 0):
                self._heartbeat_number += 1
                return

            # making a standard call to update position, etc
            req = self.api.create_request()
            req.get_player()
            if self._heartbeat_number % 10 == 0:
                req.check_awarded_badges()
                req.get_inventory()
            res = req.call()
            if not res or res.get("direction", -1) == 102:
                self.log.error(
                    "There were a problem responses for api call: %s. Restarting!!!",
                    res)
                self.api.force_refresh_access_token()
                raise AuthException("Token probably expired?")

        self.log.debug('Response dictionary: \n\r{}'.format(
            json.dumps(res, indent=2, default=lambda obj: obj.decode('utf8'))))

        responses = res.get('responses', {})
        if 'GET_PLAYER' in responses:
            self.player = Player(
                responses.get('GET_PLAYER', {}).get('player_data', {}))
            self.log.info(
                "Player Info: {0}, Pokemon Caught in this run: {1}".format(
                    self.player, self.pokemon_caught))

        if 'GET_INVENTORY' in responses:

            # update objects
            self.inventory.update_player_inventory(res=res)
            for inventory_item in self.inventory.inventory_items:
                if "player_stats" in inventory_item['inventory_item_data']:
                    self.player_stats = PlayerStats(
                        inventory_item['inventory_item_data']['player_stats'],
                        self.pokemon_caught, self.start_time, self.exp_start)
                    if self.exp_start is None:
                        self.exp_start = self.player_stats.run_exp_start
                    self.log.info("Player Stats: {}".format(self.player_stats))
            if self.config.list_inventory_before_cleanup:
                self.log.info("Player Inventory: %s", self.inventory)
            if not login_response:
                # self.log.debug(self.inventory.cleanup_inventory())
                self.inventory.cleanup_inventory()
                self.log.info("Player Inventory after cleanup: %s",
                              self.inventory)
            if self.config.list_pokemon_before_cleanup:
                self.log.info(
                    os.linesep.join(
                        map(str, self.inventory.get_caught_pokemon())))

            if not login_response:
                # maintenance
                self.incubate.incubate_eggs()
                self.inventory.use_lucky_egg()
                self.evolve.attempt_evolve()
                self.release.cleanup_pokemon()

            # save data dump
            with open("data_dumps/%s.json" % self.config.username, "w") as f:
                posf = self.get_position()
                responses['lat'] = posf[0]
                responses['lng'] = posf[1]
                responses['GET_PLAYER']['player_data'][
                    'hourly_exp'] = self.player_stats.run_hourly_exp
                f.write(
                    json.dumps(responses,
                               indent=2,
                               default=lambda obj: obj.decode('utf8')))

            # Farm precon
            if self.config.farm_items_enabled:
                pokeball_count = 0
                if not self.config.farm_ignore_pokeball_count:
                    pokeball_count += self.inventory.poke_balls
                if not self.config.farm_ignore_greatball_count:
                    pokeball_count += self.inventory.great_balls
                if not self.config.farm_ignore_ultraball_count:
                    pokeball_count += self.inventory.ultra_balls
                if not self.config.farm_ignore_masterball_count:
                    pokeball_count += self.inventory.master_balls
                if self.config.pokeball_farm_threshold > pokeball_count and not self._farm_mode_triggered:
                    self.should_catch_pokemon = False
                    self._farm_mode_triggered = True
                    self.log.info(
                        "Player only has %s Pokeballs, farming for more...",
                        pokeball_count)
                    if self.config.farm_override_step_size != -1:
                        self.step_size = self.config.farm_override_step_size
                        self.log.info("Player has changed speed to %s",
                                      self.step_size)
                elif self.config.pokeball_continue_threshold <= pokeball_count and self._farm_mode_triggered:
                    self.should_catch_pokemon = self.config.should_catch_pokemon  # Restore catch pokemon setting from config file
                    self._farm_mode_triggered = False
                    self.log.info(
                        "Player has %s Pokeballs, continuing to catch more!",
                        pokeball_count)
                    if self.config.farm_override_step_size != -1:
                        self.step_size = self.config.step_size
                        self.log.info(
                            "Player has returned to normal speed of %s",
                            self.step_size)

        if 'DOWNLOAD_SETTINGS' in responses:
            settings = responses.get('DOWNLOAD_SETTINGS',
                                     {}).get('settings', {})
            if settings.get('minimum_client_version', '0.0.0') > '0.33.0':
                self.log.error(
                    "Minimum client version has changed... the bot needs to be updated! Will now stop!"
                )
                exit(0)
            map_settings = settings.get('map_settings', {})

            get_map_objects_min_refresh_seconds = map_settings.get(
                'get_map_objects_min_refresh_seconds', 0.0)  # std. 5.0
            if get_map_objects_min_refresh_seconds != self.map_objects.get_api_rate_limit(
            ):
                self.map_objects.update_rate_limit(
                    get_map_objects_min_refresh_seconds)
            """
            fort_settings = settings.get('fort_settings', {})
            inventory_settings = settings.get('inventory_settings', {})

            get_map_objects_max_refresh_seconds = map_settings.get('get_map_objects_max_refresh_seconds', 30.0)
            get_map_objects_min_distance_meters = map_settings.get('get_map_objects_min_distance_meters', 10.0)
            encounter_range_meters = map_settings.get('encounter_range_meters', 50.0)
            poke_nav_range_meters = map_settings.get('poke_nav_range_meters', 201.0)
            pokemon_visible_range = map_settings.get('pokemon_visible_range', 70.0)
            get_map_objects_min_refresh_seconds = map_settings.get('get_map_objects_min_refresh_seconds', 5.0)
            google_maps_api_key = map_settings.get('google_maps_api_key', '')

            self.log.info('complete settings: %s', responses.get('DOWNLOAD_SETTINGS', {}))

            self.log.info('minimum_client_version: %s', str(settings.get('minimum_client_version', '0.0.0')))

            self.log.info('poke_nav_range_meters: %s', str(poke_nav_range_meters))
            self.log.info('pokemon_visible_range: %s', str(pokemon_visible_range))

            self.log.info('get_map_objects_min_refresh_seconds: %s', str(get_map_objects_min_refresh_seconds))
            self.log.info('get_map_objects_max_refresh_seconds: %s', str(get_map_objects_max_refresh_seconds))
            self.log.info('get_map_objects_min_distance_meters: %s', str(get_map_objects_min_distance_meters))
            self.log.info('encounter_range_meters: %s', str(encounter_range_meters))
            """

        self._heartbeat_number += 1
        return res

    def set_position(self, *pos):
        return self.api.set_position(*pos)

    def get_position(self):
        return self.api.get_position()

    def get_orig_position(self):
        return self._origPosF

    """ FOLLOWING ARE FUNCTIONS FOR THE WEB LISTENER """

    def release_pokemon_by_id(self, p_id):
        # acquire lock for this thread
        if self.thread_lock(persist=True):
            try:
                return self.release.do_release_pokemon_by_id(p_id)
            finally:
                # after we're done, release lock
                self.persist_lock = False
                self.thread_release()
        else:
            return 'Only one Simultaneous request allowed'

    def current_location(self):
        self.log.info("Web got position: %s", self.get_position())
        return self.get_position()

    def get_caught_pokemons(self):
        return self.inventory.get_caught_pokemon_by_family(as_json=True)

    def get_inventory(self):
        return self.inventory.to_json()

    def get_player_info(self):
        return self.player.to_json()

    def snipe_pokemon(self, lat, lng):
        # acquire lock for this thread
        if self.thread_lock(persist=True):
            try:
                return self.sniper.snipe_pokemon(float(lat), float(lng))
            finally:
                # after we're done, release lock
                self.map_objects.wait_for_api_timer()
                self.persist_lock = False
                self.thread_release()
        else:
            return 'Only one Simultaneous request allowed'

    def ping(self):
        self.log.info("Responding to ping")
        return "pong"