Example #1
0
    def _set_buddy(self, pokemon):
        response_dict = \
            self.bot.api.set_buddy_pokemon(pokemon_id=pokemon.unique_id)
        data = response_dict.get('responses', {}).get('SET_BUDDY_POKEMON', {})
        result = data.get('result', 0)

        action_delay(self.buddy_change_wait_min, self.buddy_change_wait_max)
        if result == 1:
            updated_buddy = data['updated_buddy']
            self.buddy = updated_buddy
            self.candy_awarded = 0
            self.buddy_distance_needed = pokemon.buddy_distance_needed

            self.emit_event('buddy_update',
                            formatted='{name} was set as Buddy Pokemon.',
                            data={'name': pokemon.name})
            return True
        else:
            error_codes = {
                0: 'UNSET',
                2: 'ERROR_POKEMON_DEPLOYED',
                3: 'ERROR_POKEMON_NOT_OWNED',
                4: 'ERROR_POKEMON_IS_EGG',
            }
            self.emit_event(
                'buddy_update_fail',
                formatted=
                'Error while setting {name} as Buddy Pokemon: {error}',
                data={
                    'name': pokemon.name,
                    'error': error_codes[result]
                })
            return False
Example #2
0
    def work(self):
        """
        Start the process of recycling items if necessary.
        :return: Returns whether or not the task went well
        :rtype: WorkerResult
        """

        # TODO: Use new inventory everywhere and then remove this inventory update
        inventory.refresh_inventory()

        worker_result = WorkerResult.SUCCESS
        if self.should_run():

            if not (self.max_balls_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_balls_keep, [1,2,3,4])
            if not (self.max_potions_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_potions_keep, [101,102,103,104])
            if not (self.max_berries_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_berries_keep, [701,702,703,704,705])
            if not (self.max_revives_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_revives_keep, [201,202])

            inventory.refresh_inventory()

            for item_in_inventory in inventory.items().all():

                if self.item_should_be_recycled(item_in_inventory):
                    # Make the bot appears more human
                    action_delay(self.recycle_wait_min, self.recycle_wait_max)
                    # If at any recycling process call we got an error, we consider that the result of this task is error too.
                    if ItemRecycler(self.bot, item_in_inventory, self.get_amount_to_recycle(item_in_inventory)).work() == WorkerResult.ERROR:
                        worker_result = WorkerResult.ERROR

        return worker_result
Example #3
0
    def work(self):
        """
        Start the process of recycling items if necessary.
        :return: Returns whether or not the task went well
        :rtype: WorkerResult
        """

        worker_result = WorkerResult.SUCCESS
        if self.should_run():

            if not (self.max_balls_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_balls_keep, [1,2,3,4])
            if not (self.max_potions_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_potions_keep, [101,102,103,104])
            if not (self.max_berries_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_berries_keep, [701,702,703,704,705])
            if not (self.max_revives_keep is None):
                worker_result = self.recycle_excess_category_max(self.max_revives_keep, [201,202])

            inventory.refresh_inventory()

            for item_in_inventory in inventory.items().all():

                if self.item_should_be_recycled(item_in_inventory):
                    # Make the bot appears more human
                    action_delay(self.recycle_wait_min, self.recycle_wait_max)
                    # If at any recycling process call we got an error, we consider that the result of this task is error too.
                    if ItemRecycler(self.bot, item_in_inventory, self.get_amount_to_recycle(item_in_inventory)).work() == WorkerResult.ERROR:
                        worker_result = WorkerResult.ERROR
        return worker_result
Example #4
0
    def set_buddy_pokemon(self, pokemon):
        if not self.bot.config.test:
            response_dict = self.bot.api.set_buddy_pokemon(pokemon_id=pokemon.unique_id)
        else:
            response_dict = {"responses": {"SET_BUDDY_POKEMON": {"result": SUCCESS, "updated_buddy": {"start_km_walked": 0, "last_km_awarded": 0, "id": 0}}}}

        if not response_dict:
            return False

        result = response_dict.get("responses", {}).get("SET_BUDDY_POKEMON", {}).get("result", 0)

        if result != SUCCESS:
            return False

        if not self.bot.config.test:
            self.buddy = response_dict.get("responses", {}).get("SET_BUDDY_POKEMON", {}).get("updated_buddy", {})
            self.buddyid = self._get_buddyid()

        self.emit_event("buddy_pokemon",
                        formatted="Buddy {pokemon} [IV {iv}] [CP {cp}]",
                        data={"pokemon": pokemon.name,
                              "iv": pokemon.iv,
                              "cp": pokemon.cp})

        self.lock_buddy = True

        if not self.bot.config.test:
            action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
    def get_buddy_walked(self, pokemon):
        if not self.bot.config.test:
            response_dict = self.bot.api.get_buddy_walked()
        else:
            response_dict = {"responses": {"GET_BUDDY_WALKED": {"success": True, "family_candy_id": 0, "candy_earned_count": 0}}}

        if not response_dict:
            return False

        success = response_dict.get("responses", {}).get("GET_BUDDY_WALKED", {}).get("success", False)

        if not success:
            return False

        family_candy_id = response_dict.get("responses", {}).get("GET_BUDDY_WALKED", {}).get("family_candy_id", 0)
        candy_earned_count = response_dict.get("responses", {}).get("GET_BUDDY_WALKED", {}).get("candy_earned_count", 0)
        candy = inventory.candies().get(family_candy_id)

        if not self.bot.config.test:
            candy.add(candy_earned_count)

        self.emit_event("buddy_reward",
                        formatted="Buddy {pokemon} rewards {family} candies [+{candy_earned} candies] [{candy} candies]",
                        data={"pokemon": pokemon.name,
                              "family": candy.type,
                              "candy_earned": candy_earned_count,
                              "candy": candy.quantity})

        if not self.bot.config.test:
            action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
Example #6
0
    def release_pokemon(self, pokemon):
        """

        :type pokemon: Pokemon
        """
        try:
            if self.bot.config.test:
                candy_awarded = 1
            else:
                response_dict = self.bot.api.release_pokemon(
                    pokemon_id=pokemon.id)
                candy_awarded = response_dict['responses']['RELEASE_POKEMON'][
                    'candy_awarded']
        except KeyError:
            return

        # We could refresh here too, but adding 1 saves a inventory request
        candy = inventory.candies().get(pokemon.pokemon_id)
        candy.add(candy_awarded)
        inventory.pokemons().remove(pokemon.id)
        self.bot.metrics.released_pokemon()
        self.emit_event(
            'pokemon_release',
            formatted='Exchanged {pokemon} [CP {cp}] [IV {iv}] for candy.',
            data={
                'pokemon': pokemon.name,
                'cp': pokemon.cp,
                'iv': pokemon.iv,
                'ncp': pokemon.cp_percent,
                'dps': pokemon.moveset.dps
            })
        action_delay(self.transfer_wait_min, self.transfer_wait_max)
 def release_pokemon(self, pokemon_name, cp, iv, pokemon_id):
     logger.log('Exchanging {} [CP {}] [Potential {}] for candy!'.format(pokemon_name,
                                                                         cp,
                                                                         iv), 'green')
     self.bot.api.release_pokemon(pokemon_id=pokemon_id)
     response_dict = self.bot.api.call()
     action_delay(self.bot.config.action_wait_min, self.bot.config.action_wait_max)
    def transfer_pokemon(self, pokemon):
        if self.config_transfer and (not self.bot.config.test):
            response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon.id)
        else:
            response_dict = {"responses": {"RELEASE_POKEMON": {"candy_awarded": 0}}}

        if not response_dict:
            return False

        self.emit_event("pokemon_release",
                        formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [NCP {ncp}] [DPS {dps}]",
                        data={"pokemon": pokemon.name,
                              "iv": pokemon.iv,
                              "cp": pokemon.cp,
                              "ncp": round(pokemon.ncp, 2),
                              "dps": round(pokemon.dps, 2)})

        if self.config_transfer and (not self.bot.config.test):
            candy = response_dict.get("responses", {}).get("RELEASE_POKEMON", {}).get("candy_awarded", 0)

            inventory.candies().get(pokemon.pokemon_id).add(candy)
            inventory.pokemons().remove(pokemon.id)

            action_delay(self.transfer_wait_min, self.transfer_wait_max)

        return True
Example #9
0
    def release_pokemon(self, pokemon):
        """

        :type pokemon: Pokemon
        """
        try:
            if self.bot.config.test:
                candy_awarded = 1
            else:
                response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon.id)
                candy_awarded = response_dict['responses']['RELEASE_POKEMON']['candy_awarded']
        except KeyError:
            return

        # We could refresh here too, but adding 1 saves a inventory request
        candy = inventory.candies().get(pokemon.pokemon_id)
        candy.add(candy_awarded)
        inventory.pokemons().remove(pokemon.id)
        self.bot.metrics.released_pokemon()
        self.emit_event(
            'pokemon_release',
            formatted='Exchanged {pokemon} [CP {cp}] [IV {iv}] for candy.',
            data={
                'pokemon': pokemon.name,
                'cp': pokemon.cp,
                'iv': pokemon.iv,
                'ncp': pokemon.cp_percent,
                'dps': pokemon.moveset.dps
            }
        )
        action_delay(self.bot.config.action_wait_min, self.bot.config.action_wait_max)
Example #10
0
    def set_buddy_pokemon(self, pokemon):
        if not self.bot.config.test:
            response_dict = self.bot.api.set_buddy_pokemon(pokemon_id=pokemon.unique_id)
        else:
            response_dict = {"responses": {"SET_BUDDY_POKEMON": {"result": SUCCESS, "updated_buddy": {"start_km_walked": 0, "last_km_awarded": 0, "id": 0}}}}

        if not response_dict:
            return False

        result = response_dict.get("responses", {}).get("SET_BUDDY_POKEMON", {}).get("result", 0)

        if result != SUCCESS:
            return False

        if not self.bot.config.test:
            self.buddy = response_dict.get("responses", {}).get("SET_BUDDY_POKEMON", {}).get("updated_buddy", {})
            self.buddyid = self._get_buddyid()

        self.emit_event("buddy_pokemon",
                        formatted="Buddy {pokemon} [IV {iv}] [CP {cp}]",
                        data={"pokemon": pokemon.name,
                              "iv": pokemon.iv,
                              "cp": pokemon.cp})

        self.lock_buddy = True

        if not self.bot.config.test:
            action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
    def upgrade_pokemon(self, pokemon):
        level = int(pokemon.level * 2) - 1
        candy = inventory.candies().get(pokemon.pokemon_id)

        for i in range(level, 80):
            upgrade_cost = self.pokemon_upgrade_cost[i - 1]
            upgrade_candy_cost = upgrade_cost[0]
            upgrade_stardust_cost = upgrade_cost[1]

            if self.config_upgrade and (not self.bot.config.test):
                response_dict = self.bot.api.upgrade_pokemon(
                    pokemon_id=pokemon.unique_id)
            else:
                response_dict = {
                    "responses": {
                        "UPGRADE_POKEMON": {
                            "result": SUCCESS
                        }
                    }
                }

            if not response_dict:
                return False

            result = response_dict.get("responses",
                                       {}).get("UPGRADE_POKEMON",
                                               {}).get("result", 0)

            if result != SUCCESS:
                return False

            upgrade = response_dict.get("responses", {}).get(
                "UPGRADE_POKEMON", {}).get("upgraded_pokemon", {})

            if self.config_upgrade and (not self.bot.config.test):
                candy.consume(upgrade_candy_cost)
                self.stardust_count -= upgrade_stardust_cost

            self.emit_event(
                "pokemon_upgraded",
                formatted=
                "Upgraded {pokemon} [IV {iv}] [CP {cp}] [{candy} candies] [{stardust} stardust]",
                data={
                    "pokemon": pokemon.name,
                    "iv": pokemon.iv,
                    "cp": pokemon.cp,
                    "candy": candy.quantity,
                    "stardust": self.stardust_count
                })

            if self.config_upgrade and (not self.bot.config.test):
                inventory.pokemons().remove(pokemon.unique_id)

                new_pokemon = inventory.Pokemon(upgrade)
                inventory.pokemons().add(new_pokemon)

                action_delay(self.config_transfer_wait_min,
                             self.config_transfer_wait_max)

        return True
Example #12
0
    def _revive_pokemon(self, pokemon):
        item = Item.ITEM_REVIVE.value
        amount = inventory.items().get(item).count
        if amount == 0:
            self.logger.info("No normal revives left, using MAX revive!")
            item = Item.ITEM_MAX_REVIVE.value

        amount = inventory.items().get(item).count
        if amount > 0:
            response_dict_revive = self.bot.api.use_item_revive(
                item_id=item, pokemon_id=pokemon.unique_id)
            action_delay(2, 3)
            if response_dict_revive:
                result = response_dict_revive.get('responses', {}).get(
                    'USE_ITEM_REVIVE', {}).get('result', 0)
                revive_item = inventory.items().get(item)
                # Remove the revive from the iventory
                revive_item.remove(1)
                if result is 1:  # Request success
                    self.emit_event('revived_pokemon',
                                    formatted='Revived {name}.',
                                    data={'name': pokemon.name})
                    if item == Item.ITEM_REVIVE.value:
                        pokemon.hp = int(pokemon.hp_max / 2)
                        self.to_heal.append(pokemon)
                    else:
                        # Set pokemon as revived
                        pokemon.hp = pokemon.hp_max
                    return True
                else:
                    self.emit_event('revived_pokemon',
                                    level='error',
                                    formatted='Failed to revive {name}!',
                                    data={'name': pokemon.name})
                    return False
    def release_pokemon(self, pokemon):
        """

        :type pokemon: Pokemon
        """
        try:
            if self.bot.config.test:
                candy_awarded = 1
            else:
                request = self.bot.api.create_request()
                request.release_pokemon(pokemon_id=pokemon.unique_id)
                response_dict = request.call()
                
                candy_awarded = response_dict['responses'][
                    'RELEASE_POKEMON']['candy_awarded']
        except KeyError:
            return

        # We could refresh here too, but adding 1 saves a inventory request
        candy = inventory.candies().get(pokemon.pokemon_id)
        candy.add(candy_awarded)
        inventory.pokemons().remove(pokemon.unique_id)
        self.bot.metrics.released_pokemon()
        self.emit_event(
            'pokemon_release',
            data={
                'pokemon': pokemon.name,
                'iv': pokemon.iv,
                'cp': pokemon.cp,
                'ivcp': pokemon.ivcp,
                'candy': candy.quantity,
                'candy_type': candy.type
            },
            formatted="Released {} (CP: {}, IV: {}, IVCP: {:.2f}) You now have"
                      " {} {} candies".format(pokemon.name, pokemon.cp,
                                              pokemon.iv, pokemon.ivcp,
                                              candy.quantity, candy.type)
        )
        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)''',
                             (pokemon.name, pokemon.iv, pokemon.cp))
                break
            else:
                self.emit_event(
                    'transfer_log',
                    sender=self,
                    level='info',
                    formatted="transfer_log table not found, skipping log"
                )
                break
        action_delay(self.transfer_wait_min, self.transfer_wait_max)
Example #14
0
    def release_pokemon(self, pokemon):
        """

        :type pokemon: Pokemon
        """
        try:
            if self.bot.config.test:
                candy_awarded = 1
            else:
                request = self.bot.api.create_request()
                request.release_pokemon(pokemon_id=pokemon.unique_id)
                response_dict = request.call()

                candy_awarded = response_dict['responses']['RELEASE_POKEMON'][
                    'candy_awarded']
        except KeyError:
            return

        # We could refresh here too, but adding 1 saves a inventory request
        candy = inventory.candies().get(pokemon.pokemon_id)
        candy.add(candy_awarded)
        inventory.pokemons().remove(pokemon.unique_id)
        self.bot.metrics.released_pokemon()
        self.emit_event(
            'pokemon_release',
            data={
                'pokemon': pokemon.name,
                'iv': pokemon.iv,
                'cp': pokemon.cp,
                'ivcp': pokemon.ivcp,
                'candy': candy.quantity,
                'candy_type': candy.type
            },
            formatted="Released {} (CP: {}, IV: {}, IVCP: {:.2f}) You now have"
            " {} {} candies".format(pokemon.name, pokemon.cp, pokemon.iv,
                                    pokemon.ivcp, candy.quantity, candy.type))
        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'"
            )

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute(
                    '''INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)''',
                    (pokemon.name, pokemon.iv, pokemon.cp))
                break
            else:
                self.emit_event(
                    'transfer_log',
                    sender=self,
                    level='info',
                    formatted="transfer_log table not found, skipping log")
                break
        action_delay(self.transfer_wait_min, self.transfer_wait_max)
    def _execute_pokemon_evolve(self, pokemon, cache):
        if pokemon.name in cache:
            return False

        response_dict = self.api.evolve_pokemon(pokemon_id=pokemon.unique_id)
        if response_dict.get('responses', {}).get('EVOLVE_POKEMON', {}).get('result', 0) == 1:
            xp = response_dict.get("responses", {}).get("EVOLVE_POKEMON", {}).get("experience_awarded", 0)
            evolution = response_dict.get("responses", {}).get("EVOLVE_POKEMON", {}).get("evolved_pokemon_data", {})
            awarded_candies = response_dict.get('responses', {}).get('EVOLVE_POKEMON', {}).get('candy_awarded', 0)
            candy = inventory.candies().get(pokemon.pokemon_id)

            candy.consume(pokemon.evolution_cost - awarded_candies)

            self.emit_event(
                'pokemon_evolved',
                formatted="*Evolved {}* (IV {}) (CP {}) ({} candies) (+{} xp)".format(pokemon.name, pokemon.iv, pokemon.cp, candy.quantity, xp),
                data={
                    'pokemon': pokemon.name,
                    'iv': pokemon.iv,
                    'cp': pokemon.cp,
                    'candy': candy.quantity,
                    'xp': xp,
                }
            )

            inventory.pokemons().remove(pokemon.unique_id)
            new_pokemon = inventory.Pokemon(evolution)
            inventory.pokemons().add(new_pokemon)
            inventory.player().exp += xp

            action_delay(self.min_evolve_speed, self.max_evolve_speed)
            evolve_result = True
        else:
            # cache pokemons we can't evolve. Less server calls
            cache[pokemon.name] = 1
            sleep(0.7)
            evolve_result = False

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='evolve_log'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO evolve_log (pokemon, iv, cp) VALUES (?, ?, ?)''', (pokemon.name, pokemon.iv, pokemon.cp))
                break
            else:
                self.emit_event(
                    'evolve_log',
                    sender=self,
                    level='info',
                    formatted="evolve_log table not found, skipping log"
                )
                break

        return evolve_result
Example #16
0
    def _execute_pokemon_evolve(self, pokemon, cache):
        if pokemon.name in cache:
            return False

        response_dict = self.bot.api.evolve_pokemon(pokemon_id=pokemon.unique_id)
        if response_dict.get('responses', {}).get('EVOLVE_POKEMON', {}).get('result', 0) == 1:
            xp = response_dict.get("responses", {}).get("EVOLVE_POKEMON", {}).get("experience_awarded", 0)
            evolution = response_dict.get("responses", {}).get("EVOLVE_POKEMON", {}).get("evolved_pokemon_data", {})
            awarded_candies = response_dict.get('responses', {}).get('EVOLVE_POKEMON', {}).get('candy_awarded', 0)
            candy = inventory.candies().get(pokemon.pokemon_id)

            candy.consume(pokemon.evolution_cost - awarded_candies)

            self.emit_event(
                'pokemon_evolved',
                formatted="*Evolved {}* (IV {}) (CP {}) ({} candies) (+{} xp)".format(pokemon.name, pokemon.iv, pokemon.cp, candy.quantity, xp),
                data={
                    'pokemon': pokemon.name,
                    'iv': pokemon.iv,
                    'cp': pokemon.cp,
                    'candy': candy.quantity,
                    'xp': xp,
                }
            )

            inventory.pokemons().remove(pokemon.unique_id)
            new_pokemon = inventory.Pokemon(evolution)
            inventory.pokemons().add(new_pokemon)
            inventory.player().exp += xp

            action_delay(self.min_evolve_speed, self.max_evolve_speed)
            evolve_result = True
        else:
            # cache pokemons we can't evolve. Less server calls
            cache[pokemon.name] = 1
            sleep(0.7)
            evolve_result = False

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='evolve_log'")

        result = c.fetchone()

        while True:
            if result[0] == 1:
                conn.execute('''INSERT INTO evolve_log (pokemon, iv, cp) VALUES (?, ?, ?)''', (pokemon.name, pokemon.iv, pokemon.cp))
                break
            else:
                self.emit_event(
                    'evolve_log',
                    sender=self,
                    level='info',
                    formatted="evolve_log table not found, skipping log"
                )
                break

        return evolve_result
Example #17
0
 def work(self):
     """
     Iterate over all user pokemons and nickname if needed
     """
     for pokemon in pokemons().all():  # type: Pokemon
         if not pokemon.is_favorite or not self.ignore_favorites:
             if pokemon.iv >= self.nickname_above_iv:
                 if self._nickname_pokemon(pokemon):
                     # Make the bot appears more human
                     action_delay(self.nickname_wait_min, self.nickname_wait_max)
Example #18
0
 def work(self):
     """
     Iterate over all user pokemons and nickname if needed
     """
     for pokemon in pokemons().all():  # type: Pokemon
         if not pokemon.is_favorite or not self.ignore_favorites:
             if pokemon.iv >= self.nickname_above_iv:
                 if self._nickname_pokemon(pokemon):
                     # Make the bot appears more human
                     action_delay(self.nickname_wait_min, self.nickname_wait_max)
Example #19
0
    def transfer_pokemon(self, pokemon):
        if self.config_transfer and (not self.bot.config.test):
            response_dict = self.bot.api.release_pokemon(
                pokemon_id=pokemon.unique_id)
        else:
            response_dict = {
                "responses": {
                    "RELEASE_POKEMON": {
                        "candy_awarded": 0
                    }
                }
            }

        if not response_dict:
            return False

        candy_awarded = response_dict.get("responses",
                                          {}).get("RELEASE_POKEMON",
                                                  {}).get("candy_awarded", 0)
        candy = inventory.candies().get(pokemon.pokemon_id)

        if self.config_transfer and (not self.bot.config.test):
            candy.add(candy_awarded)

        self.emit_event(
            "pokemon_release",
            formatted=
            "Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
            data={
                "pokemon": pokemon.name,
                "iv": pokemon.iv,
                "cp": pokemon.cp,
                "candy": candy.quantity
            })

        if self.config_transfer and (not self.bot.config.test):
            inventory.pokemons().remove(pokemon.unique_id)

            with self.bot.database as db:
                cursor = db.cursor()
                cursor.execute(
                    "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'"
                )

                db_result = cursor.fetchone()

                if db_result[0] == 1:
                    db.execute(
                        "INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)",
                        (pokemon.name, pokemon.iv, pokemon.cp))

            action_delay(self.config_action_wait_min,
                         self.config_action_wait_max)

        return True
 def release_pokemon(self, pokemon_name, cp, iv, pokemon_id):
     response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon_id)
     self.emit_event(
         'pokemon_release',
         formatted='Exchanged {pokemon} [CP {cp}] [IV {iv}] for candy.',
         data={
             'pokemon': pokemon_name,
             'cp': cp,
             'iv': iv
         }
     )
     action_delay(self.bot.config.action_wait_min, self.bot.config.action_wait_max)
    def get_buddy_walked(self, pokemon):
        if not self.bot.config.test:
            response_dict = self.bot.api.get_buddy_walked()
        else:
            response_dict = {
                "responses": {
                    "GET_BUDDY_WALKED": {
                        "success": True,
                        "family_candy_id": 0,
                        "candy_earned_count": 0
                    }
                }
            }

        if not response_dict:
            return False

        success = response_dict.get("responses",
                                    {}).get("GET_BUDDY_WALKED",
                                            {}).get("success", False)

        if not success:
            return False

        candy_earned_count = response_dict.get("responses", {}).get(
            "GET_BUDDY_WALKED", {}).get("candy_earned_count", 0)

        if candy_earned_count == 0:
            return

        family_candy_id = self.get_family_id(pokemon)
        candy = inventory.candies().get(family_candy_id)

        if not self.bot.config.test:
            candy.add(candy_earned_count)

        self.emit_event(
            "buddy_reward",
            formatted=
            "Buddy {pokemon} rewards {family} candies [+{candy_earned} candies] [{candy} candies]",
            data={
                "pokemon": pokemon.name,
                "family": candy.type,
                "candy_earned": candy_earned_count,
                "candy": candy.quantity
            })

        if not self.bot.config.test:
            action_delay(self.config_action_wait_min,
                         self.config_action_wait_max)

        return True
 def release_pokemon(self, pokemon_name, cp, iv, pokemon_id):
     response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon_id)
     self.bot.metrics.released_pokemon()
     self.emit_event(
         'pokemon_release',
         formatted='Exchanged {pokemon} [CP {cp}] [IV {iv}] for candy.',
         data={
             'pokemon': pokemon_name,
             'cp': cp,
             'iv': iv
         })
     action_delay(self.bot.config.action_wait_min,
                  self.bot.config.action_wait_max)
    def upgrade_pokemon(self, pokemon):
        upgrade_level = min(self.config_upgrade_level, inventory.player().level + 1.5, 40)
        candy = inventory.candies().get(pokemon.pokemon_id)

        for i in range(int(pokemon.level * 2), int(upgrade_level * 2)):
            upgrade_cost = self.pokemon_upgrade_cost[i - 2]
            upgrade_candy_cost = upgrade_cost[0]
            upgrade_stardust_cost = upgrade_cost[1]

            if self.config_upgrade and (not self.bot.config.test):
                request = self.bot.api.create_request()
                request.upgrade_pokemon(pokemon_id=pokemon.unique_id)
                response_dict = request.call()
            else:
                response_dict = {"responses": {"UPGRADE_POKEMON": {"result": SUCCESS}}}

            if not response_dict:
                return False

            result = response_dict.get("responses", {}).get("UPGRADE_POKEMON", {}).get("result", 0)

            if result != SUCCESS:
                return False

            upgrade = response_dict.get("responses", {}).get("UPGRADE_POKEMON", {}).get("upgraded_pokemon", {})

            if self.config_upgrade and (not self.bot.config.test):
                candy.consume(upgrade_candy_cost)
                self.bot.stardust -= upgrade_stardust_cost

            new_pokemon = inventory.Pokemon(upgrade)
            self.emit_event("pokemon_upgraded",
                            formatted="Upgraded {pokemon} [IV {iv}] [CP {cp} -> {new_cp}] [{candy} candies] [{stardust} stardust]",
                            data={"pokemon": pokemon.name,
                                  "iv": pokemon.iv,
                                  "cp": pokemon.cp,
                                  "new_cp": new_pokemon.cp,
                                  "candy": candy.quantity,
                                  "stardust": self.bot.stardust})

            if self.config_upgrade and (not self.bot.config.test):
                inventory.pokemons().remove(pokemon.unique_id)

                new_pokemon = inventory.Pokemon(upgrade)
                inventory.pokemons().add(new_pokemon)
                pokemon = new_pokemon

                action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
 def unfavor_pokemon(self, pokemon):
     request = self.bot.api.create_request()
     request.set_favorite_pokemon(pokemon_id=pokemon.unique_id, is_favorite=False)
     response_dict = request.call()
     
     sleep(1.2)  # wait a bit after request
     if response_dict:
         result = response_dict.get('responses', {}).get('SET_FAVORITE_POKEMON', {}).get('result', 0)
         if result is 1:  # Request success
             # Mark Pokemon as no longer favorite
             pokemon.is_favorite = False
             self.emit_event("pokemon_unfavored",
                             formatted="Unfavored {pokemon} [IV {iv}] [CP {cp}]",
                             data={"pokemon": pokemon.name,
                                   "iv": pokemon.iv,
                                   "cp": pokemon.cp})
             action_delay(self.config_action_wait_min, self.config_action_wait_max)
Example #25
0
 def recycle_excess_category_max(self, category_max, category_items_list):
     """
     Recycle the item which excess the category max
     :param category_max:
     :param category_items_list:
     :return: none:
     :rtype: None
     """
     worker_result = WorkerResult.SUCCESS
     category_inventory = self.get_category_inventory_list(category_items_list)
     category_count = 0
     for i in category_inventory:
        category_count = category_count + i[1]
     items_to_recycle = self.get_category_items_to_recycle(category_inventory, category_count, category_max)
     for item in items_to_recycle:
         action_delay(self.recycle_wait_min, self.recycle_wait_max)
         if ItemRecycler(self.bot, inventory.items().get(item[0]), item[1]).work() == WorkerResult.ERROR:
             worker_result = WorkerResult.ERROR
     return worker_result
Example #26
0
 def recycle_excess_category_max(self, category_max, category_items_list):
     """
     Recycle the item which excess the category max
     :param category_max:
     :param category_items_list:
     :return: none:
     :rtype: None
     """
     worker_result = WorkerResult.SUCCESS
     category_inventory = self.get_category_inventory_list(category_items_list)
     category_count = 0
     for i in category_inventory:
        category_count = category_count + i[1]
     items_to_recycle = self.get_category_items_to_recycle(category_inventory, category_count, category_max)
     for item in items_to_recycle:
         action_delay(self.recycle_wait_min, self.recycle_wait_max)
         if inventory.items().get(item[0]).recycle(item[1]) == WorkerResult.ERROR:
             worker_result = WorkerResult.ERROR
     return worker_result
Example #27
0
    def _revive_pokemon(self, pokemon):
        item = Item.ITEM_REVIVE.value
        amount = inventory.items().get(item).count
        if amount == 0:
            self.logger.info("No normal revives left, using MAX revive!")
            item = Item.ITEM_MAX_REVIVE.value

        amount = inventory.items().get(item).count
        if amount > 0:
            response_dict_revive = self.bot.api.use_item_revive(item_id=item, pokemon_id=pokemon.unique_id)
            action_delay(2, 3)
            if response_dict_revive:
                result = response_dict_revive.get('responses', {}).get('USE_ITEM_REVIVE', {}).get('result', 0)
                revive_item = inventory.items().get(item)
                # Remove the revive from the iventory
                revive_item.remove(1)
                if result is 1:  # Request success
                    self.emit_event(
                        'revived_pokemon',
                        formatted='Revived {name}.',
                        data={
                            'name': pokemon.name
                        }
                    )
                    if item == Item.ITEM_REVIVE.value:
                        pokemon.hp = int(pokemon.hp_max / 2)
                        self.to_heal.append(pokemon)
                    else:
                        # Set pokemon as revived
                        pokemon.hp = pokemon.hp_max
                    return True
                else:
                    self.emit_event(
                        'revived_pokemon',
                        level='error',
                        formatted='Failed to revive {name}!',
                        data={
                            'name': pokemon.name
                        }
                    )
                    return False
 def favor_pokemon(self, pokemon):
     request = self.bot.api.create_request()
     request.set_favorite_pokemon(pokemon_id=pokemon.unique_id, is_favorite=True)
     response_dict = request.call()
     
     sleep(1.2)  # wait a bit after request
     if response_dict:
         result = response_dict.get('responses', {}).get('SET_FAVORITE_POKEMON', {}).get('result', 0)
         if result is 1:  # Request success
             action_delay(self.config_action_wait_min, self.config_action_wait_max)
             # Mark Pokemon as favorite
             pokemon.is_favorite = True
             self.emit_event("pokemon_favored",
                             formatted="Favored {pokemon} [IV {iv}] [CP {cp}]",
                             data={"pokemon": pokemon.name,
                                   "iv": pokemon.iv,
                                   "cp": pokemon.cp})
         else:
             # Pokemon not found??
             self.ignore_favorite.append(pokemon.unique_id)
             pokemon.is_favorite = True
             self.logger.info("Unable to set %s as favorite!" % pokemon.name)
Example #29
0
    def _set_buddy(self, pokemon):
        request = self.bot.api.create_request()
        request.set_buddy_pokemon(pokemon_id=pokemon.unique_id)
        response_dict = request.call()
        
        data = response_dict.get('responses', {}).get('SET_BUDDY_POKEMON', {})
        result = data.get('result', 0)

        action_delay(self.buddy_change_wait_min, self.buddy_change_wait_max)
        if result == 1:
            updated_buddy = data['updated_buddy']
            self.buddy = updated_buddy
            self.candy_awarded = 0
            self.buddy_distance_needed = pokemon.buddy_distance_needed

            self.emit_event(
                'buddy_update',
                formatted='{name} was set as Buddy Pokemon.',
                data={
                    'name': pokemon.name
                }
            )
            return True
        else:
            error_codes = {
                0: 'UNSET',
                2: 'ERROR_POKEMON_DEPLOYED',
                3: 'ERROR_POKEMON_NOT_OWNED',
                4: 'ERROR_POKEMON_IS_EGG',
            }
            self.emit_event(
                'buddy_update_fail',
                formatted='Error while setting {name} as Buddy Pokemon: {error}',
                data={
                    'name': pokemon.name,
                    'error': error_codes[result]
                }
            )
            return False
Example #30
0
    def work(self):
        """
        Discard items if necessary.
        :return: Returns wether or not the task went well
        :rtype: WorkerResult
        """
        # TODO: Use new inventory everywhere and then remove the inventory update
        # Updating inventory
        inventory.refresh_inventory()
        worker_result = WorkerResult.SUCCESS
        if self.should_run():

            # For each user's item in inventory recycle it if needed
            for item_in_inventory in inventory.items().all():
                amount_to_recycle = self.get_amount_to_recycle(item_in_inventory)

                if self.item_should_be_recycled(item_in_inventory, amount_to_recycle):
                    action_delay(self.bot.config.action_wait_min, self.bot.config.action_wait_max)
                    if ItemRecycler(self.bot, item_in_inventory, amount_to_recycle).work() == WorkerResult.ERROR:
                        worker_result = WorkerResult.ERROR

        return worker_result
Example #31
0
    def transfer_pokemon(self, pokemon):
        if self.config_transfer and (not self.bot.config.test):
            response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon.unique_id)
        else:
            response_dict = {"responses": {"RELEASE_POKEMON": {"candy_awarded": 0}}}

        if not response_dict:
            return False

        candy_awarded = response_dict.get("responses", {}).get("RELEASE_POKEMON", {}).get("candy_awarded", 0)
        candy = inventory.candies().get(pokemon.pokemon_id)

        if self.config_transfer and (not self.bot.config.test):
            candy.add(candy_awarded)

        self.emit_event("pokemon_release",
                        formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
                        data={"pokemon": pokemon.name,
                              "iv": pokemon.iv,
                              "cp": pokemon.cp,
                              "candy": candy.quantity})

        if self.config_transfer and (not self.bot.config.test):
            inventory.pokemons().remove(pokemon.unique_id)

            with self.bot.database as db:
                cursor = db.cursor()
                cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

                db_result = cursor.fetchone()

                if db_result[0] == 1:
                    db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp))

            action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
    def _use_berry(self, berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball):
        # Delay to simulate selecting berry
        action_delay(self.catchsim_berry_wait_min, self.catchsim_berry_wait_max)
        new_catch_rate_by_ball = []
        self.emit_event(
            'pokemon_catch_rate',
            level='debug',
            formatted='Catch rate of {catch_rate} with {ball_name} is low. Throwing {berry_name} (have {berry_count})',
            data={
                'catch_rate': self._pct(catch_rate_by_ball[current_ball]),
                'ball_name': self.inventory.get(current_ball).name,
                'berry_name': self.inventory.get(berry_id).name,
                'berry_count': berry_count
            }
        )

        response_dict = self.api.use_item_capture(
            item_id=berry_id,
            encounter_id=encounter_id,
            spawn_point_id=self.spawn_point_guid
        )
        responses = response_dict['responses']

        if response_dict and response_dict['status_code'] == 1:

            # update catch rates using multiplier
            if 'item_capture_mult' in responses['USE_ITEM_CAPTURE']:
                for rate in catch_rate_by_ball:
                    new_catch_rate_by_ball.append(rate * responses['USE_ITEM_CAPTURE']['item_capture_mult'])
                self.emit_event(
                    'threw_berry',
                    formatted="Threw a {berry_name}! Catch rate with {ball_name} is now: {new_catch_rate}",
                    data={
                        'berry_name': self.inventory.get(berry_id).name,
                        'ball_name': self.inventory.get(current_ball).name,
                        'new_catch_rate': self._pct(new_catch_rate_by_ball[current_ball])
                    }
                )

            # softban?
            else:
                new_catch_rate_by_ball = catch_rate_by_ball
                self.bot.softban = True
                self.emit_event(
                    'softban',
                    level='warning',
                    formatted='Failed to use berry. You may be softbanned.'
                )
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'")
                result = c.fetchone()

                while True:
                    if result[0] == 1:
                        source = str("PokemonCatchWorker")
                        status = str("Possible Softban")
                        conn.execute('''INSERT INTO softban_log (status, source) VALUES (?, ?)''', (status, source))
                    break
                else:
                    self.emit_event(
                        'softban_log',
                        sender=self,
                        level='info',
                        formatted="softban_log table not found, skipping log"
                    )

        # unknown status code
        else:
            new_catch_rate_by_ball = catch_rate_by_ball
            self.emit_event(
                'threw_berry_failed',
                formatted='Unknown response when throwing berry: {status_code}.',
                data={
                    'status_code': response_dict['status_code']
                }
            )

        return new_catch_rate_by_ball
Example #33
0
    def work(self):
        forts = self.get_forts_in_range()

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)
        fort_name = details.get('name', 'Unknown')

        response_dict = self.bot.api.fort_search(
            fort_id=fort['id'],
            fort_latitude=lat,
            fort_longitude=lng,
            player_latitude=f2i(self.bot.position[0]),
            player_longitude=f2i(self.bot.position[1]))

        if ('responses' in response_dict) and ('FORT_SEARCH'
                                               in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (
                    spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(
                    response_dict)
                egg_awarded = spin_details.get('pokemon_data_egg', None)

                if egg_awarded is not None:
                    items_awarded[u'Egg'] = egg_awarded['egg_km_walked_target']

                if experience_awarded or items_awarded:
                    awards = ', '.join([
                        "{}x {}".format(items_awarded[x], x)
                        for x in items_awarded if x != u'Egg'
                    ])
                    if egg_awarded is not None:
                        awards += u', {} Egg'.format(
                            egg_awarded['egg_km_walked_target'])
                    self.emit_event(
                        'spun_pokestop',
                        formatted=
                        "Spun pokestop {pokestop}. Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'items': awards
                        })
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name})
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='pokestop_log'"
                    )
                result = c.fetchone()
                c.execute(
                    "SELECT DISTINCT COUNT(pokestop) FROM pokestop_log WHERE dated >= datetime('now','-1 day')"
                )
                if c.fetchone()[0] >= self.config.get('daily_spin_limit',
                                                      2000):
                    self.emit_event(
                        'spin_limit',
                        formatted=
                        'WARNING! You have reached your daily spin limit')
                    sys.exit(2)
                while True:
                    if result[0] == 1:
                        conn.execute(
                            '''INSERT INTO pokestop_log (pokestop, exp, items) VALUES (?, ?, ?)''',
                            (fort_name, str(experience_awarded),
                             str(items_awarded)))
                        break
                    else:
                        self.emit_event(
                            'pokestop_log',
                            sender=self,
                            level='info',
                            formatted=
                            "pokestop_log table not found, skipping log")
                        break
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [
                    fort['id']
                ]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event('pokestop_out_of_range',
                                formatted="Pokestop {pokestop} out of range.",
                                data={'pokestop': fort_name})
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update(
                        {fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time((pokestop_cooldown / 1000) -
                                               seconds_since_epoch)
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted=
                        "Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={
                            'pokestop': fort_name,
                            'minutes_left': minutes_left
                        })
            else:
                self.emit_event('unknown_spin_result',
                                formatted="Unknown spint result {status_code}",
                                data={'status_code': str(spin_result)})
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest.")
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event('softban',
                                    formatted='Probably got softban.')
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute(
                            "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'"
                        )
                    result = c.fetchone()

                    if result[0] == 1:
                        source = str("PokemonCatchWorker")
                        status = str("Possible Softban")
                        conn.execute(
                            '''INSERT INTO softban_log (status, source) VALUES (?, ?)''',
                            (status, source))
                    else:
                        self.emit_event(
                            'softban_log',
                            sender=self,
                            level='info',
                            formatted=
                            "softban_log table not found, skipping log")

                self.bot.fort_timeouts[fort["id"]] = (
                    time.time() + 300) * 1000  # Don't spin for 5m

                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS
    def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False):
        # settings that may be exposed at some point
        """

        :type pokemon: Pokemon
        """
        berry_id = ITEM_RAZZBERRY
        maximum_ball = ITEM_ULTRABALL if is_vip else ITEM_GREATBALL
        ideal_catch_rate_before_throw = 0.9 if is_vip else 0.35

        berry_count = self.inventory.get(ITEM_RAZZBERRY).count
        ball_count = {}
        for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
            ball_count[ball_id] = self.inventory.get(ball_id).count

        # use `min_ultraball_to_keep` from config if is not None
        min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
        if self.min_ultraball_to_keep is not None:
            if self.min_ultraball_to_keep >= 0 and self.min_ultraball_to_keep < min_ultraball_to_keep:
                min_ultraball_to_keep = self.min_ultraball_to_keep

        while True:

            # find lowest available ball
            current_ball = ITEM_POKEBALL
            while ball_count[current_ball] == 0 and current_ball < maximum_ball:
                current_ball += 1
            if ball_count[current_ball] == 0:
                self.emit_event('no_pokeballs', formatted='No usable pokeballs found!')

                # use untraball if there is no other balls with constraint to `min_ultraball_to_keep`
                if maximum_ball != ITEM_ULTRABALL and ball_count[ITEM_ULTRABALL] > min_ultraball_to_keep:
                    maximum_ball = ITEM_ULTRABALL
                    continue
                else:
                    break

            # check future ball count
            num_next_balls = 0
            next_ball = current_ball
            while next_ball < maximum_ball:
                next_ball += 1
                num_next_balls += ball_count[next_ball]

            # check if we've got berries to spare
            berries_to_spare = berry_count > 0 if is_vip else berry_count > num_next_balls + 30

            # use a berry if we are under our ideal rate and have berries to spare
            used_berry = False
            changed_ball = False
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berries_to_spare:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # pick the best ball to catch with
            best_ball = current_ball
            while best_ball < maximum_ball:
                best_ball += 1
                if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and ball_count[best_ball] > 0:
                    # if current ball chance to catch is under our ideal rate, and player has better ball - then use it
                    current_ball = best_ball
                    changed_ball = True

            # if the rate is still low and we didn't throw a berry before, throw one
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # If we change ball then wait to simulate user selecting it
            if changed_ball:
                action_delay(self.catchsim_changeball_wait_min, self.catchsim_changeball_wait_max)

            # Randomize the quality of the throw
            # Default structure
            throw_parameters = {'normalized_reticle_size': 1.950,
                                'spin_modifier': 1.0,
                                'normalized_hit_position': 1.0,
                                'throw_type_label': 'Excellent'}
            self.generate_spin_parameter(throw_parameters)
            self.generate_throw_quality_parameters(throw_parameters)

            # try to catch pokemon!
            # TODO : Log which type of throw we selected
            ball_count[current_ball] -= 1
            self.inventory.get(current_ball).remove(1)
            # Take some time to throw the ball from config options
            action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
            self.emit_event(
                'threw_pokeball',
                formatted='Used {ball_name}, with chance {success_percentage} ({count_left} left)',
                data={
                    'ball_name': self.inventory.get(current_ball).name,
                    'success_percentage': self._pct(catch_rate_by_ball[current_ball]),
                    'count_left': ball_count[current_ball]
                }
            )

            response_dict = self.api.catch_pokemon(
                encounter_id=encounter_id,
                pokeball=current_ball,
                normalized_reticle_size=throw_parameters['normalized_reticle_size'],
                spawn_point_id=self.spawn_point_guid,
                hit_pokemon=1,
                spin_modifier=throw_parameters['spin_modifier'],
                normalized_hit_position=throw_parameters['normalized_hit_position']
            )

            try:
                catch_pokemon_status = response_dict['responses']['CATCH_POKEMON']['status']
            except KeyError:
                break

            # retry failed pokemon
            if catch_pokemon_status == CATCH_STATUS_FAILED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='{pokemon} capture failed.. trying again!',
                    data={'pokemon': pokemon.name}
                )

                # sleep according to flee_count and flee_duration config settings
                # randomly chooses a number of times to 'show' wobble animation between 1 and flee_count
                # multiplies this by flee_duration to get total sleep
                if self.catchsim_flee_count:
                    sleep((randrange(self.catchsim_flee_count)+1) * self.catchsim_flee_duration)

                continue

            # abandon if pokemon vanished
            elif catch_pokemon_status == CATCH_STATUS_VANISHED:
                self.emit_event(
                    'pokemon_vanished',
                    formatted='{pokemon} vanished!',
                    data={
                        'pokemon': pokemon.name,
                        'encounter_id': self.pokemon['encounter_id'],
                        'latitude': self.pokemon['latitude'],
                        'longitude': self.pokemon['longitude'],
                        'pokemon_id': pokemon.pokemon_id
                    }
                )
                if self._pct(catch_rate_by_ball[current_ball]) == 100:
                    self.bot.softban = True

            # pokemon caught!
            elif catch_pokemon_status == CATCH_STATUS_SUCCESS:
                pokemon.id = response_dict['responses']['CATCH_POKEMON']['captured_pokemon_id']
                self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp, pokemon.iv_display, pokemon.iv)
                inventory.pokemons().add(pokemon)
                self.emit_event(
                    'pokemon_caught',
                    formatted='Captured {pokemon}! [CP {cp}] [Potential {iv}] [{iv_display}] [+{exp} exp]',
                    data={
                        'pokemon': pokemon.name,
                        'cp': pokemon.cp,
                        'iv': pokemon.iv,
                        'iv_display': pokemon.iv_display,
                        'exp': sum(response_dict['responses']['CATCH_POKEMON']['capture_award']['xp']),
                        'encounter_id': self.pokemon['encounter_id'],
                        'latitude': self.pokemon['latitude'],
                        'longitude': self.pokemon['longitude'],
                        'pokemon_id': pokemon.pokemon_id
                    }
                )

                # We could refresh here too, but adding 3 saves a inventory request
                candy = inventory.candies(True).get(pokemon.pokemon_id)
                self.emit_event(
                    'gained_candy',
                    formatted='You now have {quantity} {type} candy!',
                    data = {
                        'quantity': candy.quantity,
                        'type': candy.type,
                    },
                )

                self.bot.softban = False

            break
    def _use_berry(self, berry_id, berry_count, encounter_id,
                   catch_rate_by_ball, current_ball):
        # Delay to simulate selecting berry
        action_delay(self.catchsim_berry_wait_min,
                     self.catchsim_berry_wait_max)
        new_catch_rate_by_ball = []
        self.emit_event(
            'pokemon_catch_rate',
            level='debug',
            formatted=
            'Catch rate of {catch_rate} with {ball_name} is low. Throwing {berry_name} (have {berry_count})',
            data={
                'catch_rate': self._pct(catch_rate_by_ball[current_ball]),
                'ball_name': self.inventory.get(current_ball).name,
                'berry_name': self.inventory.get(berry_id).name,
                'berry_count': berry_count
            })

        response_dict = self.api.use_item_capture(
            item_id=berry_id,
            encounter_id=encounter_id,
            spawn_point_id=self.spawn_point_guid)
        responses = response_dict['responses']

        if response_dict and response_dict['status_code'] == 1:

            # update catch rates using multiplier
            if 'item_capture_mult' in responses['USE_ITEM_CAPTURE']:
                for rate in catch_rate_by_ball:
                    new_catch_rate_by_ball.append(
                        rate *
                        responses['USE_ITEM_CAPTURE']['item_capture_mult'])
                self.emit_event(
                    'threw_berry',
                    formatted=
                    "Threw a {berry_name}! Catch rate with {ball_name} is now: {new_catch_rate}",
                    data={
                        'berry_name':
                        self.inventory.get(berry_id).name,
                        'ball_name':
                        self.inventory.get(current_ball).name,
                        'new_catch_rate':
                        self._pct(new_catch_rate_by_ball[current_ball])
                    })

            # softban?
            else:
                new_catch_rate_by_ball = catch_rate_by_ball
                self.bot.softban = True
                self.emit_event(
                    'softban',
                    level='warning',
                    formatted='Failed to use berry. You may be softbanned.')

        # unknown status code
        else:
            new_catch_rate_by_ball = catch_rate_by_ball
            self.emit_event(
                'threw_berry_failed',
                formatted=
                'Unknown response when throwing berry: {status_code}.',
                data={'status_code': response_dict['status_code']})

        return new_catch_rate_by_ball
    def _do_catch(self,
                  pokemon,
                  encounter_id,
                  catch_rate_by_ball,
                  is_vip=False):
        # settings that may be exposed at some point
        """

        :type pokemon: Pokemon
        """
        berry_id = ITEM_RAZZBERRY
        maximum_ball = ITEM_ULTRABALL if is_vip else ITEM_GREATBALL
        ideal_catch_rate_before_throw = 0.9 if is_vip else 0.35

        berry_count = self.inventory.get(ITEM_RAZZBERRY).count
        ball_count = {}
        for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
            ball_count[ball_id] = self.inventory.get(ball_id).count

        # use `min_ultraball_to_keep` from config if is not None
        min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
        if self.min_ultraball_to_keep is not None:
            if self.min_ultraball_to_keep >= 0 and self.min_ultraball_to_keep < min_ultraball_to_keep:
                min_ultraball_to_keep = self.min_ultraball_to_keep

        while True:

            # find lowest available ball
            current_ball = ITEM_POKEBALL
            while ball_count[current_ball] == 0 and current_ball < maximum_ball:
                current_ball += 1
            if ball_count[current_ball] == 0:
                self.emit_event('no_pokeballs',
                                formatted='No usable pokeballs found!')

                # use untraball if there is no other balls with constraint to `min_ultraball_to_keep`
                if maximum_ball != ITEM_ULTRABALL and ball_count[
                        ITEM_ULTRABALL] > min_ultraball_to_keep:
                    maximum_ball = ITEM_ULTRABALL
                    continue
                else:
                    break

            # check future ball count
            num_next_balls = 0
            next_ball = current_ball
            while next_ball < maximum_ball:
                next_ball += 1
                num_next_balls += ball_count[next_ball]

            # check if we've got berries to spare
            berries_to_spare = berry_count > 0 if is_vip else berry_count > num_next_balls + 30

            # use a berry if we are under our ideal rate and have berries to spare
            used_berry = False
            changed_ball = False
            if catch_rate_by_ball[
                    current_ball] < ideal_catch_rate_before_throw and berries_to_spare:
                new_catch_rate_by_ball = self._use_berry(
                    berry_id, berry_count, encounter_id, catch_rate_by_ball,
                    current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # pick the best ball to catch with
            best_ball = current_ball
            while best_ball < maximum_ball:
                best_ball += 1
                if catch_rate_by_ball[
                        current_ball] < ideal_catch_rate_before_throw and ball_count[
                            best_ball] > 0:
                    # if current ball chance to catch is under our ideal rate, and player has better ball - then use it
                    current_ball = best_ball
                    changed_ball = True

            # if the rate is still low and we didn't throw a berry before, throw one
            if catch_rate_by_ball[
                    current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry:
                new_catch_rate_by_ball = self._use_berry(
                    berry_id, berry_count, encounter_id, catch_rate_by_ball,
                    current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # If we change ball then wait to simulate user selecting it
            if changed_ball:
                action_delay(self.catchsim_changeball_wait_min,
                             self.catchsim_changeball_wait_max)

            # Randomize the quality of the throw
            # Default structure
            throw_parameters = {
                'normalized_reticle_size': 1.950,
                'spin_modifier': 1.0,
                'normalized_hit_position': 1.0,
                'throw_type_label': 'Excellent'
            }
            self.generate_spin_parameter(throw_parameters)
            self.generate_throw_quality_parameters(throw_parameters)

            # try to catch pokemon!
            # TODO : Log which type of throw we selected
            ball_count[current_ball] -= 1
            self.inventory.get(current_ball).remove(1)
            # Take some time to throw the ball from config options
            action_delay(self.catchsim_catch_wait_min,
                         self.catchsim_catch_wait_max)
            self.emit_event(
                'threw_pokeball',
                formatted=
                'Used {ball_name}, with chance {success_percentage} ({count_left} left)',
                data={
                    'ball_name':
                    self.inventory.get(current_ball).name,
                    'success_percentage':
                    self._pct(catch_rate_by_ball[current_ball]),
                    'count_left':
                    ball_count[current_ball]
                })

            response_dict = self.api.catch_pokemon(
                encounter_id=encounter_id,
                pokeball=current_ball,
                normalized_reticle_size=throw_parameters[
                    'normalized_reticle_size'],
                spawn_point_id=self.spawn_point_guid,
                hit_pokemon=1,
                spin_modifier=throw_parameters['spin_modifier'],
                normalized_hit_position=throw_parameters[
                    'normalized_hit_position'])

            try:
                catch_pokemon_status = response_dict['responses'][
                    'CATCH_POKEMON']['status']
            except KeyError:
                break

            # retry failed pokemon
            if catch_pokemon_status == CATCH_STATUS_FAILED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='{pokemon} capture failed.. trying again!',
                    data={'pokemon': pokemon.name})

                # sleep according to flee_count and flee_duration config settings
                # randomly chooses a number of times to 'show' wobble animation between 1 and flee_count
                # multiplies this by flee_duration to get total sleep
                if self.catchsim_flee_count:
                    sleep((randrange(self.catchsim_flee_count) + 1) *
                          self.catchsim_flee_duration)

                continue

            # abandon if pokemon vanished
            elif catch_pokemon_status == CATCH_STATUS_VANISHED:
                self.emit_event('pokemon_vanished',
                                formatted='{pokemon} vanished!',
                                data={
                                    'pokemon': pokemon.name,
                                    'encounter_id':
                                    self.pokemon['encounter_id'],
                                    'latitude': self.pokemon['latitude'],
                                    'longitude': self.pokemon['longitude'],
                                    'pokemon_id': pokemon.pokemon_id
                                })
                if self._pct(catch_rate_by_ball[current_ball]) == 100:
                    self.bot.softban = True

            # pokemon caught!
            elif catch_pokemon_status == CATCH_STATUS_SUCCESS:
                pokemon.id = response_dict['responses']['CATCH_POKEMON'][
                    'captured_pokemon_id']
                self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp,
                                                  pokemon.iv_display,
                                                  pokemon.iv)
                inventory.pokemons().add(pokemon)
                self.emit_event(
                    'pokemon_caught',
                    formatted=
                    'Captured {pokemon}! [CP {cp}] [Potential {iv}] [{iv_display}] [+{exp} exp]',
                    data={
                        'pokemon':
                        pokemon.name,
                        'cp':
                        pokemon.cp,
                        'iv':
                        pokemon.iv,
                        'iv_display':
                        pokemon.iv_display,
                        'exp':
                        sum(response_dict['responses']['CATCH_POKEMON']
                            ['capture_award']['xp']),
                        'encounter_id':
                        self.pokemon['encounter_id'],
                        'latitude':
                        self.pokemon['latitude'],
                        'longitude':
                        self.pokemon['longitude'],
                        'pokemon_id':
                        pokemon.pokemon_id
                    })

                # We could refresh here too, but adding 3 saves a inventory request
                candy = inventory.candies(True).get(pokemon.pokemon_id)
                self.emit_event(
                    'gained_candy',
                    formatted='You now have {quantity} {type} candy!',
                    data={
                        'quantity': candy.quantity,
                        'type': candy.type,
                    },
                )

                self.bot.softban = False

            break
Example #37
0
    def work(self):
        forts = self.get_forts_in_range()

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)
        fort_name = details.get('name', 'Unknown')

        response_dict = self.bot.api.fort_search(
            fort_id=fort['id'],
            fort_latitude=lat,
            fort_longitude=lng,
            player_latitude=f2i(self.bot.position[0]),
            player_longitude=f2i(self.bot.position[1])
        )

        if ('responses' in response_dict) and ('FORT_SEARCH' in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(response_dict)

                if experience_awarded or items_awarded:
                    self.emit_event(
                        'spun_pokestop',
                        formatted="Spun pokestop {pokestop}. Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'items': items_awarded
                        }
                    )
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name}
                    )
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [fort['id']]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event(
                    'pokestop_out_of_range',
                    formatted="Pokestop {pokestop} out of range.",
                    data={'pokestop': fort_name}
                )
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time(
                        (pokestop_cooldown / 1000) - seconds_since_epoch
                    )
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted="Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={'pokestop': fort_name, 'minutes_left': minutes_left}
                    )
            else:
                self.emit_event(
                    'unknown_spin_result',
                    formatted="Unknown spint result {status_code}",
                    data={'status_code': str(spin_result)}
                )
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest."
                )
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event(
                        'softban',
                        formatted='Probably got softban.'
                    )
                else:
                    self.bot.fort_timeouts[fort["id"]] = (time.time() + 300) * 1000  # Don't spin for 5m
                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS
Example #38
0
    def _use_berry(self, berry_id, berry_count, encounter_id,
                   catch_rate_by_ball, current_ball):
        # Delay to simulate selecting berry
        action_delay(self.catchsim_berry_wait_min,
                     self.catchsim_berry_wait_max)
        new_catch_rate_by_ball = []
        self.emit_event(
            'pokemon_catch_rate',
            level='debug',
            formatted=
            'Catch rate of {catch_rate} with {ball_name} is low. Throwing {berry_name} (have {berry_count})',
            data={
                'catch_rate': self._pct(catch_rate_by_ball[current_ball]),
                'ball_name': self.inventory.get(current_ball).name,
                'berry_name': self.inventory.get(berry_id).name,
                'berry_count': berry_count
            })

        response_dict = self.bot.api.use_item_capture(
            item_id=berry_id,
            encounter_id=encounter_id,
            spawn_point_id=self.spawn_point_guid)
        responses = response_dict['responses']

        if response_dict and response_dict['status_code'] == 1:

            # update catch rates using multiplier
            if 'item_capture_mult' in responses['USE_ITEM_CAPTURE']:
                for rate in catch_rate_by_ball:
                    new_catch_rate_by_ball.append(
                        rate *
                        responses['USE_ITEM_CAPTURE']['item_capture_mult'])
                self.emit_event(
                    'threw_berry',
                    formatted=
                    "Threw a {berry_name}! Catch rate with {ball_name} is now: {new_catch_rate}",
                    data={
                        'berry_name':
                        self.inventory.get(berry_id).name,
                        'ball_name':
                        self.inventory.get(current_ball).name,
                        'new_catch_rate':
                        self._pct(new_catch_rate_by_ball[current_ball])
                    })

            # softban?
            else:
                new_catch_rate_by_ball = catch_rate_by_ball
                self.bot.softban = True
                self.emit_event(
                    'softban',
                    level='warning',
                    formatted='Failed to use berry. You may be softbanned.')
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'"
                    )
                result = c.fetchone()

                while True:
                    if result[0] == 1:
                        source = str("PokemonCatchWorker")
                        status = str("Possible Softban")
                        conn.execute(
                            '''INSERT INTO softban_log (status, source) VALUES (?, ?)''',
                            (status, source))
                    break
                else:
                    self.emit_event(
                        'softban_log',
                        sender=self,
                        level='info',
                        formatted="softban_log table not found, skipping log")

        # unknown status code
        else:
            new_catch_rate_by_ball = catch_rate_by_ball
            self.emit_event(
                'threw_berry_failed',
                formatted=
                'Unknown response when throwing berry: {status_code}.',
                data={'status_code': response_dict['status_code']})

        return new_catch_rate_by_ball
    def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False):
        # settings that may be exposed at some point
        """

        :type pokemon: Pokemon
        """

        if self.use_pinap_on_vip and is_vip and pokemon.level <= self.pinap_on_level_below and self.pinap_operator == "and":
            berry_id = ITEM_PINAPBERRY
        else:
            berry_id = ITEM_RAZZBERRY
        
        if self.pinap_operator == "or":
            if (self.use_pinap_on_vip and is_vip) or (pokemon.level <= self.pinap_on_level_below):
                berry_id = ITEM_PINAPBERRY

        berry_count = self.inventory.get(berry_id).count

        ball_count = {}
        for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
            ball_count[ball_id] = self.inventory.get(ball_id).count

        # use `min_ultraball_to_keep` from config if is not None
        min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
        if self.min_ultraball_to_keep is not None and self.min_ultraball_to_keep >= 0:
            min_ultraball_to_keep = self.min_ultraball_to_keep

        maximum_ball = ITEM_GREATBALL if ball_count[ITEM_ULTRABALL] < min_ultraball_to_keep else ITEM_ULTRABALL
        ideal_catch_rate_before_throw = self.vip_berry_threshold if is_vip else self.berry_threshold
        ideal_catch_rate_before_throw = 1 if self.pinap_ignore_threshold and berry_id == ITEM_PINAPBERRY else ideal_catch_rate_before_throw

        used_berry = False
        original_catch_rate_by_ball = catch_rate_by_ball
        
        if DEBUG_ON:
            print "Pokemon Level: " + str(pokemon.level) + " Berries count: " + str(berry_count) + " Berries ID: " + str(berry_id) + " Catch rate: " + str(ideal_catch_rate_before_throw)
        
        while True:

            # find lowest available ball
            current_ball = ITEM_POKEBALL
            while ball_count[current_ball] == 0 and current_ball < maximum_ball:
                current_ball += 1
            if ball_count[current_ball] == 0:
                self.emit_event('no_pokeballs', formatted='No pokeballs left! Fleeing...')
                return WorkerResult.ERROR

            # check future ball count
            num_next_balls = 0
            next_ball = current_ball
            while next_ball < maximum_ball:
                next_ball += 1
                num_next_balls += ball_count[next_ball]

            # If pinap berry is not enough, use razz berry
            if berry_count == 0 and berry_id == ITEM_PINAPBERRY:
                berry_id = ITEM_RAZZBERRY
                ideal_catch_rate_before_throw = self.vip_berry_threshold if is_vip else self.berry_threshold
                berry_count = self.inventory.get(berry_id).count

            # check if we've got berries to spare
            berries_to_spare = berry_count > 0 if is_vip else berry_count > num_next_balls + 30

            changed_ball = False
            
            # Golden Razz: Use golden razz
            if (self.use_golden_razz_on_vip_only==is_vip) or (self.use_golden_razz_on_vip_only == False):
                # Golden Razz: Use golden razz when catch rate is low, keep some for raid
                if (self.inventory.get(ITEM_GOLDEN_RAZZ_BERRY).count > self.golden_razz_to_keep and catch_rate_by_ball[current_ball] < self.golden_razz_threshold):
                    berry_id = ITEM_GOLDEN_RAZZ_BERRY
                    berry_count = self.inventory.get(berry_id).count
                    new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                    self.inventory.get(berry_id).remove(1)
                    berry_count -= 1
                    used_berry = True

            # SMART_PINAP: Use pinap when high catch rate, but spare some for VIP with high catch rate
            if self.smart_pinap_enabled and ( (not is_vip and self.inventory.get(ITEM_PINAPBERRY).count > self.smart_pinap_to_keep and catch_rate_by_ball[current_ball] > self.smart_pinap_threshold) or (is_vip and self.inventory.get(ITEM_PINAPBERRY).count > 0 and catch_rate_by_ball[current_ball] >= self.vip_berry_threshold) ) and not used_berry:
                berry_id = ITEM_PINAPBERRY
                berry_count = self.inventory.get(berry_id).count
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                self.inventory.get(berry_id).remove(1)
                berry_count -= 1
                used_berry = True

				# use a berry if we are under our ideal rate and have berries to spare
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berries_to_spare and not used_berry:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(berry_id).remove(1)
                    berry_count -= 1
                    used_berry = True

            # pick the best ball to catch with
            best_ball = current_ball
            while best_ball < maximum_ball:
                best_ball += 1
                if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and ball_count[best_ball] > 0:
                    # if current ball chance to catch is under our ideal rate, and player has better ball - then use it
                    current_ball = best_ball
                    changed_ball = True

            # if the rate is still low and we didn't throw a berry before, throw one
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(berry_id).remove(1)
                    berry_count -= 1
                    used_berry = True

            # If we change ball then wait to simulate user selecting it
            if changed_ball:
                action_delay(self.catchsim_changeball_wait_min, self.catchsim_changeball_wait_max)

            # Randomize the quality of the throw
            # Default structure
            throw_parameters = {'normalized_reticle_size': 1.950,
                                'spin_modifier': 1.0,
                                'normalized_hit_position': 1.0,
                                'throw_type_label': 'Excellent'}
            self.generate_spin_parameter(throw_parameters)
            self.generate_throw_quality_parameters(throw_parameters)

            # try to catch pokemon!
            ball_count[current_ball] -= 1
            self.inventory.get(current_ball).remove(1)
            # Take some time to throw the ball from config options
            action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
            self.emit_event(
                'threw_pokeball',
                formatted='{throw_type}{spin_label} throw! Used {ball_name}, with chance {success_percentage} ({count_left} left)',
                data={
                    'throw_type': throw_parameters['throw_type_label'],
                    'spin_label': throw_parameters['spin_label'],
                    'ball_name': self.inventory.get(current_ball).name,
                    'success_percentage': self._pct(catch_rate_by_ball[current_ball]),
                    'count_left': ball_count[current_ball]
                }
            )

            hit_pokemon = 1
            if random() >= self.catch_throw_parameters_hit_rate and not is_vip:
                hit_pokemon = 0
            
            request = self.bot.api.create_request()
            request.catch_pokemon(
                encounter_id=encounter_id,
                pokeball=current_ball,
                normalized_reticle_size=throw_parameters['normalized_reticle_size'],
                spawn_point_id=self.spawn_point_guid,
                hit_pokemon=hit_pokemon,
                spin_modifier=throw_parameters['spin_modifier'],
                normalized_hit_position=throw_parameters['normalized_hit_position']
            )
            response_dict = request.call()
            
            try:
                catch_pokemon_status = response_dict['responses']['CATCH_POKEMON']['status']
            except KeyError:
                break

            # retry failed pokemon
            if catch_pokemon_status == CATCH_STATUS_FAILED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='{pokemon} capture failed.. trying again!',
                    data={'pokemon': pokemon.name}
                )
                used_berry = False
                catch_rate_by_ball = original_catch_rate_by_ball

                # sleep according to flee_count and flee_duration config settings
                # randomly chooses a number of times to 'show' wobble animation between 1 and flee_count
                # multiplies this by flee_duration to get total sleep
                if self.catchsim_flee_count:
                    sleep((randrange(self.catchsim_flee_count)+1) * self.catchsim_flee_duration)

                continue

            # abandon if pokemon vanished
            elif catch_pokemon_status == CATCH_STATUS_VANISHED:
                #insert into DB
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='vanish_log'")
                result = c.fetchone()

                while True:
                    if result[0] == 1:
                        conn.execute('''INSERT INTO vanish_log (pokemon, cp, iv, encounter_id, pokemon_id) VALUES (?, ?, ?, ?, ?)''', (pokemon.name, pokemon.cp, pokemon.iv, str(encounter_id), pokemon.pokemon_id))
                    break
                else:
                    self.emit_event(
                        'vanish_log',
                        sender=self,
                        level='info',
                        formatted="vanish_log table not found, skipping log"
                    )
                    break

                self.emit_event(
                    'pokemon_vanished',
                    formatted='{} vanished!'.format(pokemon.name),
                    data={
                        'pokemon': pokemon.name,
                        'encounter_id': self.pokemon['encounter_id'],
                        'latitude': self.pokemon['latitude'],
                        'longitude': self.pokemon['longitude'],
                        'pokemon_id': pokemon.pokemon_id
                    }
                )

                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT DISTINCT COUNT(encounter_id) FROM vanish_log WHERE dated > (SELECT dated FROM catch_log WHERE dated IN (SELECT MAX(dated) FROM catch_log))")

                result = c.fetchone()
                self.consecutive_vanishes_so_far = result[0]

                if self.rest_completed == False and self.consecutive_vanishes_so_far >= self.consecutive_vanish_limit:
                    self.start_rest()

                if self._pct(catch_rate_by_ball[current_ball]) == 100:
                    self.bot.softban = True

            # pokemon caught!
            elif catch_pokemon_status == CATCH_STATUS_SUCCESS:
                if self.rest_completed == True:
                    self.rest_completed = False
                pokemon.unique_id = response_dict['responses']['CATCH_POKEMON']['captured_pokemon_id']
                self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp, pokemon.iv_display, pokemon.iv)

                awards = response_dict['responses']['CATCH_POKEMON']['capture_award']
                exp_gain, candy_gain, stardust_gain = self.extract_award(awards)
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT DISTINCT COUNT(encounter_id) FROM catch_log WHERE dated >= datetime('now','-1 day')")

                result = c.fetchone()

                if is_vip:
                    self.emit_event(
                        'pokemon_vip_caught',
                        formatted='Vip Captured {pokemon}! (CP: {cp} IV: {iv} {iv_display} NCP: {ncp} Shiny: {shiny}) Catch Limit: ({caught_last_24_hour}/{daily_catch_limit}) +{exp} exp +{stardust} stardust',
                        data={
                            'pokemon': pokemon.name,
                            'ncp': str(round(pokemon.cp_percent, 2)),
                            'cp': str(int(pokemon.cp)),
                            'iv': str(pokemon.iv),
                            'iv_display': str(pokemon.iv_display),
                            'shiny': pokemon.shiny,
                            'exp': str(exp_gain),
                            'stardust': stardust_gain,
                            'encounter_id': str(self.pokemon['encounter_id']),
                            'latitude': str(self.pokemon['latitude']),
                            'longitude': str(self.pokemon['longitude']),
                            'pokemon_id': str(pokemon.pokemon_id),
                            'caught_last_24_hour': str(result[0]),
                            'daily_catch_limit': str(self.daily_catch_limit)
                        }
                    )

                else:
                    self.emit_event(
                        'pokemon_caught',
                        formatted='Captured {pokemon}! (CP: {cp} IV: {iv} {iv_display} NCP: {ncp} Shiny: {shiny}) Catch Limit: ({caught_last_24_hour}/{daily_catch_limit}) +{exp} exp +{stardust} stardust',
                        data={
                            'pokemon': pokemon.name,
                            'ncp': str(round(pokemon.cp_percent, 2)),
                            'cp': str(int(pokemon.cp)),
                            'iv': str(pokemon.iv),
                            'iv_display': str(pokemon.iv_display),
                            'shiny': pokemon.shiny,
                            'exp': str(exp_gain),
                            'stardust': stardust_gain,
                            'encounter_id': str(self.pokemon['encounter_id']),
                            'latitude': str(self.pokemon['latitude']),
                            'longitude': str(self.pokemon['longitude']),
                            'pokemon_id': str(pokemon.pokemon_id),
                            'caught_last_24_hour': str(result[0]),
                            'daily_catch_limit': str(self.daily_catch_limit)
                        }
                    )


                inventory.pokemons().add(pokemon)
                inventory.player().exp += exp_gain
                self.bot.stardust += stardust_gain
                candy = inventory.candies().get(pokemon.pokemon_id)
                candy.add(candy_gain)

                self.emit_event(
                    'gained_candy',
                    formatted='Candy gained: {gained_candy}. You now have {quantity} {type} candy!',
                    data = {
                        'gained_candy': str(candy_gain),
                        'quantity': candy.quantity,
                        'type': candy.type
                    },
                )

                self.bot.softban = False


                try:
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='catch_log'")
                    result = c.fetchone()

                    while True:
                        if result[0] == 1:
                            conn.execute('''INSERT INTO catch_log (pokemon, cp, iv, encounter_id, pokemon_id) VALUES (?, ?, ?, ?, ?)''', (pokemon.name, pokemon.cp, pokemon.iv, str(encounter_id), pokemon.pokemon_id))
                        break
                    else:
                        self.emit_event(
                            'catch_log',
                            sender=self,
                            level='info',
                            formatted="catch_log table not found, skipping log"
                        )
                        break
                    user_data_caught = os.path.join(_base_dir, 'data', 'caught-%s.json' % self.bot.config.username)
                    with open(user_data_caught, 'ab') as outfile:
                        json.dump(OrderedDict({
                            'datetime': str(datetime.now()),
                            'pokemon': pokemon.name,
                            'cp': pokemon.cp,
                            'iv': pokemon.iv,
                            'encounter_id': self.pokemon['encounter_id'],
                            'pokemon_id': pokemon.pokemon_id,
                            'latitude': self.pokemon['latitude'],
                            'longitude': self.pokemon['longitude']
                        }), outfile)
                        outfile.write('\n')

                    # if it is a new pokemon to our dex, simulate app animation delay
                    if exp_gain >= 500:
                        sleep (randrange(self.catchsim_newtodex_wait_min, self.catchsim_newtodex_wait_max))

                except IOError as e:
                    self.logger.info('[x] Error while opening location file: %s' % e)

            elif catch_pokemon_status == CATCH_STATUS_MISSED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='Pokeball thrown to {pokemon} missed.. trying again!',
                    data={'pokemon': pokemon.name}
                )
                # Take some time to throw the ball from config options
                action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
                continue

            break
    def _do_catch(self, pokemon, encounter_id, catch_rate_by_ball, is_vip=False):
        # settings that may be exposed at some point
        """

        :type pokemon: Pokemon
        """
        berry_id = ITEM_RAZZBERRY
        maximum_ball = ITEM_ULTRABALL if is_vip else ITEM_GREATBALL
        ideal_catch_rate_before_throw = self.vip_berry_threshold if is_vip else self.berry_threshold

        berry_count = self.inventory.get(ITEM_RAZZBERRY).count
        ball_count = {}
        for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
            ball_count[ball_id] = self.inventory.get(ball_id).count

        # use `min_ultraball_to_keep` from config if is not None
        min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
        if self.min_ultraball_to_keep is not None:
            if self.min_ultraball_to_keep >= 0 and self.min_ultraball_to_keep < min_ultraball_to_keep:
                min_ultraball_to_keep = self.min_ultraball_to_keep

        used_berry = False
        while True:

            # find lowest available ball
            current_ball = ITEM_POKEBALL
            while ball_count[current_ball] == 0 and current_ball < maximum_ball:
                current_ball += 1
            if ball_count[current_ball] == 0:
                self.emit_event('no_pokeballs', formatted='No usable pokeballs found!')

                # use untraball if there is no other balls with constraint to `min_ultraball_to_keep`
                if maximum_ball != ITEM_ULTRABALL and ball_count[ITEM_ULTRABALL] > min_ultraball_to_keep:
                    maximum_ball = ITEM_ULTRABALL
                    continue
                else:
                    return WorkerResult.ERROR

            # check future ball count
            num_next_balls = 0
            next_ball = current_ball
            while next_ball < maximum_ball:
                next_ball += 1
                num_next_balls += ball_count[next_ball]

            # check if we've got berries to spare
            berries_to_spare = berry_count > 0 if is_vip else berry_count > num_next_balls + 30

            # use a berry if we are under our ideal rate and have berries to spare
            changed_ball = False
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berries_to_spare and not used_berry:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # pick the best ball to catch with
            best_ball = current_ball
            while best_ball < maximum_ball:
                best_ball += 1
                if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and ball_count[best_ball] > 0:
                    # if current ball chance to catch is under our ideal rate, and player has better ball - then use it
                    current_ball = best_ball
                    changed_ball = True

            # if the rate is still low and we didn't throw a berry before, throw one
            if catch_rate_by_ball[current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry:
                new_catch_rate_by_ball = self._use_berry(berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # If we change ball then wait to simulate user selecting it
            if changed_ball:
                action_delay(self.catchsim_changeball_wait_min, self.catchsim_changeball_wait_max)

            # Randomize the quality of the throw
            # Default structure
            throw_parameters = {'normalized_reticle_size': 1.950,
                                'spin_modifier': 1.0,
                                'normalized_hit_position': 1.0,
                                'throw_type_label': 'Excellent'}
            self.generate_spin_parameter(throw_parameters)
            self.generate_throw_quality_parameters(throw_parameters)

            # try to catch pokemon!
            ball_count[current_ball] -= 1
            self.inventory.get(current_ball).remove(1)
            # Take some time to throw the ball from config options
            action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
            self.emit_event(
                'threw_pokeball',
                formatted='{throw_type}{spin_label} throw! Used {ball_name}, with chance {success_percentage} ({count_left} left)',
                data={
                    'throw_type': throw_parameters['throw_type_label'],
                    'spin_label': throw_parameters['spin_label'],
                    'ball_name': self.inventory.get(current_ball).name,
                    'success_percentage': self._pct(catch_rate_by_ball[current_ball]),
                    'count_left': ball_count[current_ball]
                }
            )

            hit_pokemon = 1
            if random() >= self.catch_throw_parameters_hit_rate and not is_vip:
                hit_pokemon = 0

            response_dict = self.api.catch_pokemon(
                encounter_id=encounter_id,
                pokeball=current_ball,
                normalized_reticle_size=throw_parameters['normalized_reticle_size'],
                spawn_point_id=self.spawn_point_guid,
                hit_pokemon=hit_pokemon,
                spin_modifier=throw_parameters['spin_modifier'],
                normalized_hit_position=throw_parameters['normalized_hit_position']
            )

            try:
                catch_pokemon_status = response_dict['responses']['CATCH_POKEMON']['status']
            except KeyError:
                break

            # retry failed pokemon
            if catch_pokemon_status == CATCH_STATUS_FAILED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='{pokemon} capture failed.. trying again!',
                    data={'pokemon': pokemon.name}
                )
                used_berry = False

                # sleep according to flee_count and flee_duration config settings
                # randomly chooses a number of times to 'show' wobble animation between 1 and flee_count
                # multiplies this by flee_duration to get total sleep
                if self.catchsim_flee_count:
                    sleep((randrange(self.catchsim_flee_count)+1) * self.catchsim_flee_duration)

                continue

            # abandon if pokemon vanished
            elif catch_pokemon_status == CATCH_STATUS_VANISHED:
                self.emit_event(
                    'pokemon_vanished',
                    formatted='{pokemon} vanished!',
                    data={
                        'pokemon': pokemon.name,
                        'encounter_id': self.pokemon['encounter_id'],
                        'latitude': self.pokemon['latitude'],
                        'longitude': self.pokemon['longitude'],
                        'pokemon_id': pokemon.pokemon_id
                    }
                )
                if self._pct(catch_rate_by_ball[current_ball]) == 100:
                    self.bot.softban = True

            # pokemon caught!
            elif catch_pokemon_status == CATCH_STATUS_SUCCESS:
                pokemon.unique_id = response_dict['responses']['CATCH_POKEMON']['captured_pokemon_id']
                self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp, pokemon.iv_display, pokemon.iv)

                try:
                    inventory.pokemons().add(pokemon)
                    self.emit_event(
                        'pokemon_caught',
                        formatted='Captured {pokemon}! [CP {cp}] [NCP {ncp}] [Potential {iv}] [{iv_display}] [+{exp} exp]',
                        data={
                            'pokemon': pokemon.name,
                            'ncp': round(pokemon.cp_percent, 2),
                            'cp': pokemon.cp,
                            'iv': pokemon.iv,
                            'iv_display': pokemon.iv_display,
                            'exp': sum(response_dict['responses']['CATCH_POKEMON']['capture_award']['xp']),
                            'encounter_id': self.pokemon['encounter_id'],
                            'latitude': self.pokemon['latitude'],
                            'longitude': self.pokemon['longitude'],
                            'pokemon_id': pokemon.pokemon_id
                        }

                    )
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='catch_log'")
                    result = c.fetchone()

                    while True:
                        if result[0] == 1:
                            conn.execute('''INSERT INTO catch_log (pokemon, cp, iv, encounter_id, pokemon_id) VALUES (?, ?, ?, ?, ?)''', (pokemon.name, pokemon.cp, pokemon.iv, str(encounter_id), pokemon.pokemon_id))
                        break
                    else:
                        self.emit_event(
                            'catch_log',
                            sender=self,
                            level='info',
                            formatted="catch_log table not found, skipping log"
                        )
                        break
                    user_data_caught = os.path.join(_base_dir, 'data', 'caught-%s.json' % self.bot.config.username)
                    with open(user_data_caught, 'ab') as outfile:
                        outfile.write(str(datetime.now()))
                        json.dump({
                            'pokemon': pokemon.name,
                            'cp': pokemon.cp,
                            'iv': pokemon.iv,
                            'encounter_id': self.pokemon['encounter_id'],
                            'pokemon_id': pokemon.pokemon_id
                        }, outfile)
                        outfile.write('\n')

                except IOError as e:
                    self.logger.info('[x] Error while opening location file: %s' % e)

                candy = inventory.candies().get(pokemon.pokemon_id)
                candy.add(self.get_candy_gained_count(response_dict))

                self.emit_event(
                    'gained_candy',
                    formatted='You now have {quantity} {type} candy!',
                    data = {
                        'quantity': candy.quantity,
                        'type': candy.type,
                    },
                )

                self.bot.softban = False

            elif catch_pokemon_status == CATCH_STATUS_MISSED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='Pokeball thrown to {pokemon} missed.. trying again!',
                    data={'pokemon': pokemon.name}
                )
                # Take some time to throw the ball from config options
                action_delay(self.catchsim_catch_wait_min, self.catchsim_catch_wait_max)
                continue

            break
    def transfer_pokemon(self, pokemons, skip_delay=False):
        error_codes = {
            0: 'UNSET',
            1: 'SUCCESS',
            2: 'POKEMON_DEPLOYED',
            3: 'FAILED',
            4: 'ERROR_POKEMON_IS_EGG',
            5: 'ERROR_POKEMON_IS_BUDDY'
        }
        if self.config_bulktransfer_enabled and len(pokemons) > 1:
            while len(pokemons) > 0:
                action_delay(self.config_action_wait_min, self.config_action_wait_max)
                pokemon_ids = []
                count = 0
                transfered = []
                while len(pokemons) > 0 and count < self.config_max_bulktransfer:
                    pokemon = pokemons.pop()
                    transfered.append(pokemon)
                    pokemon_ids.append(pokemon.unique_id)
                    count = count + 1
                try:
                    if self.config_transfer:
                        request = self.bot.api.create_request()
                        request.release_pokemon(pokemon_ids=pokemon_ids)
                        response_dict = request.call()
                        
                        result = response_dict['responses']['RELEASE_POKEMON']['result']
                        if result != 1:
                            self.logger.error(u'Error while transfer pokemon: {}'.format(error_codes[result]))
                            return False
                except Exception:
                    return False

                for pokemon in transfered:
                    candy = inventory.candies().get(pokemon.pokemon_id)

                    if self.config_transfer and (not self.bot.config.test):
                        candy.add(1)

                    self.emit_event("pokemon_release",
                                    formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
                                    data={"pokemon": pokemon.name,
                                          "iv": pokemon.iv,
                                          "cp": pokemon.cp,
                                          "candy": candy.quantity})

                    if self.config_transfer:
                        inventory.pokemons().remove(pokemon.unique_id)

                        with self.bot.database as db:
                            cursor = db.cursor()
                            cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

                            db_result = cursor.fetchone()

                            if db_result[0] == 1:
                                db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp))

        else:
            for pokemon in pokemons:
                if self.config_transfer and (not self.bot.config.test):
                    request = self.bot.api.create_request()
                    request.release_pokemon(pokemon_id=pokemon.unique_id)
                    response_dict = request.call()
                else:
                    response_dict = {"responses": {"RELEASE_POKEMON": {"candy_awarded": 0}}}

                if not response_dict:
                    return False

                candy_awarded = response_dict.get("responses", {}).get("RELEASE_POKEMON", {}).get("candy_awarded", 0)
                candy = inventory.candies().get(pokemon.pokemon_id)

                if self.config_transfer and (not self.bot.config.test):
                    candy.add(candy_awarded)

                self.emit_event("pokemon_release",
                                formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
                                data={"pokemon": pokemon.name,
                                      "iv": pokemon.iv,
                                      "cp": pokemon.cp,
                                      "candy": candy.quantity})

                if self.config_transfer and (not self.bot.config.test):
                    inventory.pokemons().remove(pokemon.unique_id)

                    with self.bot.database as db:
                        cursor = db.cursor()
                        cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

                        db_result = cursor.fetchone()

                        if db_result[0] == 1:
                            db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp))
                    if not skip_delay:
                        action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
    def _use_berry(self, berry_id, berry_count, encounter_id, catch_rate_by_ball, current_ball):
        # Delay to simulate selecting berry
        action_delay(self.catchsim_berry_wait_min, self.catchsim_berry_wait_max)
        new_catch_rate_by_ball = []
        self.emit_event(
            'pokemon_catch_rate',
            level='debug',
            formatted='Catch rate of {catch_rate} with {ball_name} is low. Throwing {berry_name} (have {berry_count})',
            data={
                'catch_rate': self._pct(catch_rate_by_ball[current_ball]),
                'ball_name': self.inventory.get(current_ball).name,
                'berry_name': self.inventory.get(berry_id).name,
                'berry_count': berry_count
            }
        )

        response_dict = self.api.use_item_capture(
            item_id=berry_id,
            encounter_id=encounter_id,
            spawn_point_id=self.spawn_point_guid
        )
        responses = response_dict['responses']

        if response_dict and response_dict['status_code'] == 1:

            # update catch rates using multiplier
            if 'item_capture_mult' in responses['USE_ITEM_CAPTURE']:
                for rate in catch_rate_by_ball:
                    new_catch_rate_by_ball.append(rate * responses['USE_ITEM_CAPTURE']['item_capture_mult'])
                self.emit_event(
                    'threw_berry',
                    formatted="Threw a {berry_name}! Catch rate with {ball_name} is now: {new_catch_rate}",
                    data={
                        'berry_name': self.inventory.get(berry_id).name,
                        'ball_name': self.inventory.get(current_ball).name,
                        'new_catch_rate': self._pct(new_catch_rate_by_ball[current_ball])
                    }
                )

            # softban?
            else:
                new_catch_rate_by_ball = catch_rate_by_ball
                self.bot.softban = True
                self.emit_event(
                    'softban',
                    level='warning',
                    formatted='Failed to use berry. You may be softbanned.'
                )

        # unknown status code
        else:
            new_catch_rate_by_ball = catch_rate_by_ball
            self.emit_event(
                'threw_berry_failed',
                formatted='Unknown response when throwing berry: {status_code}.',
                data={
                    'status_code': response_dict['status_code']
                }
            )

        return new_catch_rate_by_ball
Example #43
0
    def _heal_pokemon(self, pokemon):
        if pokemon.hp == 0:
            self.logger.info("Can't heal a dead %s" % pokemon.name)
            return False
        # normal = inventory.items().get(Item.ITEM_POTION.value).count
        # super_p = inventory.items().get(Item.ITEM_SUPER_POTION.value).count
        # hyper = inventory.items().get(Item.ITEM_HYPER_POTION.value).count
        max_p = inventory.items().get(Item.ITEM_MAX_POTION.value).count

        # Figure out how much healing needs to be done.
        def hp_to_restore(pokemon):
            pokemon = inventory.pokemons().get_from_unique_id(
                pokemon.unique_id)
            return pokemon.hp_max - pokemon.hp

        if hp_to_restore(pokemon) > 200 and max_p > 0:
            # We should use a MAX Potion
            self._use_potion(Item.ITEM_MAX_POTION.value, pokemon)
            pokemon.hp = pokemon.hp_max
            return True
        # Okay, now we see to heal as effective as possible
        potions = [103, 102, 101]
        heals = [200, 50, 20]

        for item_id, max_heal in zip(potions, heals):
            if inventory.items().get(item_id).count > 0:
                while hp_to_restore(pokemon) > max_heal:
                    if inventory.items().get(item_id).count == 0:
                        break
                    action_delay(2, 3)
                    # More than 200 to restore, use a hyper first
                    if self._use_potion(item_id, pokemon):
                        pokemon.hp += max_heal
                        if pokemon.hp > pokemon.hp_max:
                            pokemon.hp = pokemon.hp_max
                    else:
                        break
                        # return WorkerResult.ERROR

        # Now we use the least
        potion_id = 101  # Normals first
        while hp_to_restore(pokemon) > 0:
            action_delay(2, 4)
            if inventory.items().get(potion_id).count > 0:
                if potion_id == 104:
                    self.logger.info("Using MAX potion to heal a %s" %
                                     pokemon.name)
                if self._use_potion(potion_id, pokemon):
                    if potion_id == 104:
                        pokemon.hp = pokemon.hp_max
                    else:
                        pokemon.hp += heals[potion_id - 101]
                        if pokemon.hp > pokemon.hp_max:
                            pokemon.hp = pokemon.hp_max
                else:
                    if potion_id < 104:
                        self.logger.info(
                            "Failed with potion %s. Trying next." % potion_id)
                        potion_id += 1
                    else:
                        self.logger.info("Failed with MAX potion. Done.")
                        break
            elif potion_id < 104:
                potion_id += 1
            else:
                self.logger.info("Can't heal a %s" % pokemon.name)
                break
Example #44
0
    def work(self):
        forts = self.get_forts_in_range()

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute("SELECT DISTINCT COUNT(pokestop) FROM pokestop_log WHERE dated >= datetime('now','-1 day')")
        if c.fetchone()[0] >= self.config.get('daily_spin_limit', 2000):
           if self.exit_on_limit_reached:
               self.emit_event('spin_limit', formatted='WARNING! You have reached your daily spin limit')
               sys.exit(2)

           if datetime.now() >= self.next_update:
               self.emit_event('spin_limit', formatted='WARNING! You have reached your daily spin limit')
               self._compute_next_update()
               return WorkerResult.SUCCESS

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        if fort['id'] in self.streak_forts:
            self.fort_spins = 1
            self.streak_forts = [fort['id']]
        elif self.fort_spins >= 10:
            self.fort_spins = 1
            self.streak_forts = [fort['id']]
        else:
            self.fort_spins += 1

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)

        fort_name = details.get('name', 'Unknown')
        check_fort_modifier = details.get('modifiers', {})
        if self.use_lure and check_fort_modifier:
            # check_fort_modifier_id = check_fort_modifier[0].get('item_id')
            self.emit_event('lure_info', formatted='A lure is already in fort, skip deploying lure')

        if self.use_lure and not check_fort_modifier:
            # check lures availiblity
            lure_count = inventory.items().get(501).count

            if lure_count > 1: # Only use lures when there's more than one
                request = self.bot.api.create_request()
                request.add_fort_modifier(
                    modifier_type=501,
                    fort_id = fort['id'],
                    player_latitude = f2i(self.bot.position[0]),
                    player_longitude = f2i(self.bot.position[1])
                )
                response_dict = request.call()

                if ('responses' in response_dict) and ('ADD_FORT_MODIFIER' in response_dict['responses']):
                    add_modifier_deatils = response_dict['responses']['ADD_FORT_MODIFIER']
                    add_modifier_result = add_modifier_deatils.get('result', -1)
                    if (add_modifier_result == LURE_REQUEST_RESULT_SUCCESS):
                        self.emit_event('lure_success', formatted='You have successfully placed a lure')
                    if (add_modifier_result == LURE_REQUEST_FORT_ALREADY_HAS_MODIFIER):
                        self.emit_event('lure_failed', formatted='A lure has being placed before you try to do so')
                    if (add_modifier_result == LURE_REQUEST_TOO_FAR_AWAY):
                        self.emit_event('lure_failed', formatted='Pokestop out of range')
                    if (add_modifier_result == LURE_REQUEST_NO_ITEM_IN_INVENTORY):
                        self.emit_event('lure_not_enough', formatted='Not enough lure in inventory')
                    if (add_modifier_result == LURE_REQUEST_POI_INACCESSIBLE):
                        self.emit_event('lure_info', formatted='Unkown Error')
            else:
                self.emit_event('lure_not_enough', formatted='Not enough lure in inventory')

        request = self.bot.api.create_request()
        request.fort_search(
            fort_id=fort['id'],
            fort_latitude=lat,
            fort_longitude=lng,
            player_latitude=f2i(self.bot.position[0]),
            player_longitude=f2i(self.bot.position[1])
        )
        response_dict = request.call()

        if ('responses' in response_dict) and ('FORT_SEARCH' in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(response_dict)
                egg_awarded = spin_details.get('pokemon_data_egg', None)
                gym_badge_awarded = spin_details.get('awarded_gym_badge', None)
                chain_hack_sequence_number = spin_details.get('chain_hack_sequence_number', 0)

                if egg_awarded is not None:
                    items_awarded[u'Egg'] = egg_awarded['egg_km_walked_target']

                # if gym_badge_awarded is not None:
                #     self.logger.info("Gained a Gym Badge! %s" % gym_badge_awarded)
                #
                # if chain_hack_sequence_number > 0:
                #     self.logger.info("Chain hack sequence: %s" % chain_hack_sequence_number)

                if experience_awarded or items_awarded:
                    awards = ', '.join(["{}x {}".format(items_awarded[x], x) for x in items_awarded if x != u'Egg'])
                    if egg_awarded is not None:
                        awards += u', {} Egg'.format(egg_awarded['egg_km_walked_target'])
                    self.fort_spins = chain_hack_sequence_number

                    if "type" in fort and fort["type"] == 1:
                        # It's a Pokestop
                        stop_kind = "pokestop"
                    else:
                        # It's a gym
                        stop_kind = "gym"

                    self.emit_event(
                        'spun_pokestop',
                        formatted="Spun {stop_kind} {pokestop} ({spin_amount_now} streak). Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'stop_kind': stop_kind,
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'spin_amount_now': chain_hack_sequence_number,
                            'items': awards
                        }
                    )
                    #time.sleep(10)
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name}
                    )
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='pokestop_log'")
                result = c.fetchone()
                while True:
                    if result[0] == 1:
                        conn.execute('''INSERT INTO pokestop_log (pokestop, exp, items) VALUES (?, ?, ?)''', (fort_name, str(experience_awarded), str(items_awarded)))
                        break
                    else:
                        self.emit_event('pokestop_log',
                                        sender=self,
                                        level='info',
                                        formatted="pokestop_log table not found, skipping log")
                        break
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [fort['id']]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event(
                    'pokestop_out_of_range',
                    formatted="Pokestop {pokestop} out of range.",
                    data={'pokestop': fort_name}
                )
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time(
                        (pokestop_cooldown / 1000) - seconds_since_epoch
                    )
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted="Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={'pokestop': fort_name, 'minutes_left': minutes_left}
                    )
            elif spin_result == SPIN_REQUEST_RESULT_POI_INACCESSIBLE:
                self.logger.info("Pokestop not accessable at this time.")
                self.bot.fort_timeouts[fort["id"]] = (time.time() + 300) * 1000  # Don't spin for 5m
            else:
                self.emit_event(
                    'unknown_spin_result',
                    formatted="Unknown spint result {status_code}",
                    data={'status_code': str(spin_result)}
                )
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest."
                )
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event(
                        'softban',
                        formatted='Probably got softban.'
                    )
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'")
                    result = c.fetchone()

                    if result[0] == 1:
                        source = str("SpinFort")
                        status = str("Possible Softban")
                        conn.execute('''INSERT INTO softban_log (status, source) VALUES (?, ?)''', (status, source))
                    else:
                        self.emit_event('softban_log',
                                        sender=self,
                                        level='info',
                                        formatted="softban_log table not found, skipping log")

                self.bot.fort_timeouts[fort["id"]] = (time.time() + 300) * 1000  # Don't spin for 5m

                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS
Example #45
0
    def transfer_pokemon(self, pokemons):
        error_codes = {
            0: 'UNSET',
            1: 'SUCCESS',
            2: 'POKEMON_DEPLOYED',
            3: 'FAILED',
            4: 'ERROR_POKEMON_IS_EGG',
            5: 'ERROR_POKEMON_IS_BUDDY'
        }
        if self.config_bulktransfer_enabled and len(pokemons) > 1:
            while len(pokemons) > 0:
                action_delay(self.config_action_wait_min, self.config_action_wait_max)
                pokemon_ids = []
                count = 0
                transfered = []
                while len(pokemons) > 0 and count < self.config_max_bulktransfer:
                    pokemon = pokemons.pop()
                    transfered.append(pokemon)
                    pokemon_ids.append(pokemon.unique_id)
                    count = count + 1
                try:
                    if self.config_transfer:
                        response_dict = self.bot.api.release_pokemon(pokemon_ids=pokemon_ids)
                        result = response_dict['responses']['RELEASE_POKEMON']['result']
                        if result != 1:
                            self.logger.error(u'Error while transfer pokemon: {}'.format(error_codes[result]))
                            return False
                except Exception:
                    return False
                
                for pokemon in transfered:
                    candy = inventory.candies().get(pokemon.pokemon_id)

                    if self.config_transfer and (not self.bot.config.test):
                        candy.add(1)
                        
                    self.emit_event("pokemon_release",
                                    formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
                                    data={"pokemon": pokemon.name,
                                          "iv": pokemon.iv,
                                          "cp": pokemon.cp,
                                          "candy": candy.quantity})

                    if self.config_transfer:
                        inventory.pokemons().remove(pokemon.unique_id)

                        with self.bot.database as db:
                            cursor = db.cursor()
                            cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

                            db_result = cursor.fetchone()

                            if db_result[0] == 1:
                                db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp))

        else:
            for pokemon in pokemons:
                if self.config_transfer and (not self.bot.config.test):
                    response_dict = self.bot.api.release_pokemon(pokemon_id=pokemon.unique_id)
                else:
                    response_dict = {"responses": {"RELEASE_POKEMON": {"candy_awarded": 0}}}

                if not response_dict:
                    return False

                candy_awarded = response_dict.get("responses", {}).get("RELEASE_POKEMON", {}).get("candy_awarded", 0)
                candy = inventory.candies().get(pokemon.pokemon_id)

                if self.config_transfer and (not self.bot.config.test):
                    candy.add(candy_awarded)

                self.emit_event("pokemon_release",
                                formatted="Exchanged {pokemon} [IV {iv}] [CP {cp}] [{candy} candies]",
                                data={"pokemon": pokemon.name,
                                      "iv": pokemon.iv,
                                      "cp": pokemon.cp,
                                      "candy": candy.quantity})

                if self.config_transfer and (not self.bot.config.test):
                    inventory.pokemons().remove(pokemon.unique_id)

                    with self.bot.database as db:
                        cursor = db.cursor()
                        cursor.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='transfer_log'")

                        db_result = cursor.fetchone()

                        if db_result[0] == 1:
                            db.execute("INSERT INTO transfer_log (pokemon, iv, cp) VALUES (?, ?, ?)", (pokemon.name, pokemon.iv, pokemon.cp))

                    action_delay(self.config_action_wait_min, self.config_action_wait_max)

        return True
Example #46
0
    def work(self):
        forts = self.get_forts_in_range()

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)
        fort_name = details.get('name', 'Unknown')

        response_dict = self.bot.api.fort_search(
            fort_id=fort['id'],
            fort_latitude=lat,
            fort_longitude=lng,
            player_latitude=f2i(self.bot.position[0]),
            player_longitude=f2i(self.bot.position[1]))

        if ('responses' in response_dict) and ('FORT_SEARCH'
                                               in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (
                    spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(
                    response_dict)

                if experience_awarded or items_awarded:
                    self.emit_event(
                        'spun_pokestop',
                        formatted=
                        "Spun pokestop {pokestop}. Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'items': items_awarded
                        })
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name})
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [
                    fort['id']
                ]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event('pokestop_out_of_range',
                                formatted="Pokestop {pokestop} out of range.",
                                data={'pokestop': fort_name})
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update(
                        {fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time((pokestop_cooldown / 1000) -
                                               seconds_since_epoch)
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted=
                        "Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={
                            'pokestop': fort_name,
                            'minutes_left': minutes_left
                        })
            else:
                self.emit_event('unknown_spin_result',
                                formatted="Unknown spint result {status_code}",
                                data={'status_code': str(spin_result)})
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest.")
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event('softban',
                                    formatted='Probably got softban.')
                else:
                    self.bot.fort_timeouts[fort["id"]] = (
                        time.time() + 300) * 1000  # Don't spin for 5m
                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS
Example #47
0
    def work(self):
        forts = self.get_forts_in_range()

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute("SELECT DISTINCT COUNT(pokestop) FROM pokestop_log WHERE dated >= datetime('now','-1 day')")
        if c.fetchone()[0] >= self.config.get('daily_spin_limit', 2000):
           if self.exit_on_limit_reached:
               self.emit_event('spin_limit', formatted='WARNING! You have reached your daily spin limit')
               sys.exit(2)

           if datetime.now() >= self.next_update:
               self.emit_event('spin_limit', formatted='WARNING! You have reached your daily spin limit')
               self._compute_next_update()
               return WorkerResult.SUCCESS

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)
        fort_name = details.get('name', 'Unknown')

        response_dict = self.bot.api.fort_search(
            fort_id=fort['id'],
            fort_latitude=lat,
            fort_longitude=lng,
            player_latitude=f2i(self.bot.position[0]),
            player_longitude=f2i(self.bot.position[1])
        )

        if ('responses' in response_dict) and ('FORT_SEARCH' in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(response_dict)
                egg_awarded = spin_details.get('pokemon_data_egg', None)

                if egg_awarded is not None:
                    items_awarded[u'Egg'] = egg_awarded['egg_km_walked_target']

                if experience_awarded or items_awarded:
                    awards = ', '.join(["{}x {}".format(items_awarded[x], x) for x in items_awarded if x != u'Egg'])
                    if egg_awarded is not None:
                        awards += u', {} Egg'.format(egg_awarded['egg_km_walked_target'])
                    self.emit_event(
                        'spun_pokestop',
                        formatted="Spun pokestop {pokestop}. Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'items': awards
                        }
                    )
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name}
                    )
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='pokestop_log'")
                result = c.fetchone()
                while True:
                    if result[0] == 1:
                        conn.execute('''INSERT INTO pokestop_log (pokestop, exp, items) VALUES (?, ?, ?)''', (fort_name, str(experience_awarded), str(items_awarded)))
                        break
                    else:
                        self.emit_event('pokestop_log',
                                        sender=self,
                                        level='info',
                                        formatted="pokestop_log table not found, skipping log")
                        break
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [fort['id']]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event(
                    'pokestop_out_of_range',
                    formatted="Pokestop {pokestop} out of range.",
                    data={'pokestop': fort_name}
                )
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time(
                        (pokestop_cooldown / 1000) - seconds_since_epoch
                    )
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted="Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={'pokestop': fort_name, 'minutes_left': minutes_left}
                    )
            else:
                self.emit_event(
                    'unknown_spin_result',
                    formatted="Unknown spint result {status_code}",
                    data={'status_code': str(spin_result)}
                )
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest."
                )
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event(
                        'softban',
                        formatted='Probably got softban.'
                    )
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute("SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'")
                    result = c.fetchone()

                    if result[0] == 1:
                        source = str("PokemonCatchWorker")
                        status = str("Possible Softban")
                        conn.execute('''INSERT INTO softban_log (status, source) VALUES (?, ?)''', (status, source))
                    else:
                        self.emit_event('softban_log',
                                        sender=self,
                                        level='info',
                                        formatted="softban_log table not found, skipping log")

                self.bot.fort_timeouts[fort["id"]] = (time.time() + 300) * 1000  # Don't spin for 5m

                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS
Example #48
0
    def _heal_pokemon(self, pokemon):
        if pokemon.hp == 0:
            self.logger.info("Can't heal a dead %s" % pokemon.name)
            return False
        # normal = inventory.items().get(Item.ITEM_POTION.value).count
        # super_p = inventory.items().get(Item.ITEM_SUPER_POTION.value).count
        # hyper = inventory.items().get(Item.ITEM_HYPER_POTION.value).count
        max_p = inventory.items().get(Item.ITEM_MAX_POTION.value).count
        # Figure out how much healing needs to be done.
        def hp_to_restore(pokemon):
            pokemon = inventory.pokemons().get_from_unique_id(pokemon.unique_id)
            return pokemon.hp_max - pokemon.hp

        if hp_to_restore(pokemon) > 200 and max_p > 0:
            # We should use a MAX Potion
            self._use_potion(Item.ITEM_MAX_POTION.value, pokemon)
            pokemon.hp = pokemon.hp_max
            return True
        # Okay, now we see to heal as effective as possible
        potions = [103, 102, 101]
        heals = [200, 50, 20]

        for item_id, max_heal in zip(potions, heals):
            if inventory.items().get(item_id).count > 0:
                while hp_to_restore(pokemon) > max_heal:
                    if inventory.items().get(item_id).count == 0:
                        break
                    action_delay(2, 3)
                    # More than 200 to restore, use a hyper first
                    if self._use_potion(item_id, pokemon):
                        pokemon.hp += max_heal
                        if pokemon.hp > pokemon.hp_max:
                            pokemon.hp = pokemon.hp_max
                    else:
                        break
                        # return WorkerResult.ERROR

        # Now we use the least
        potion_id = 101 # Normals first
        while hp_to_restore(pokemon) > 0:
            action_delay(2, 4)
            if inventory.items().get(potion_id).count > 0:
                if potion_id == 104:
                    self.logger.info("Using MAX potion to heal a %s" % pokemon.name)
                if self._use_potion(potion_id, pokemon):
                    if potion_id == 104:
                        pokemon.hp = pokemon.hp_max
                    else:
                        pokemon.hp += heals[potion_id - 101]
                        if pokemon.hp > pokemon.hp_max:
                            pokemon.hp = pokemon.hp_max
                else:
                    if potion_id < 104:
                        self.logger.info("Failed with potion %s. Trying next." % potion_id)
                        potion_id += 1
                    else:
                        self.logger.info("Failed with MAX potion. Done.")
                        break
            elif potion_id < 104:
                potion_id += 1
            else:
                self.logger.info("Can't heal a %s" % pokemon.name)
                break
Example #49
0
    def _do_catch(self,
                  pokemon,
                  encounter_id,
                  catch_rate_by_ball,
                  is_vip=False):
        # settings that may be exposed at some point
        """

        :type pokemon: Pokemon
        """
        berry_count = self.inventory.get(ITEM_RAZZBERRY).count
        ball_count = {}
        for ball_id in [ITEM_POKEBALL, ITEM_GREATBALL, ITEM_ULTRABALL]:
            ball_count[ball_id] = self.inventory.get(ball_id).count

        # use `min_ultraball_to_keep` from config if is not None
        min_ultraball_to_keep = ball_count[ITEM_ULTRABALL]
        if self.min_ultraball_to_keep is not None and self.min_ultraball_to_keep >= 0:
            min_ultraball_to_keep = self.min_ultraball_to_keep

        berry_id = ITEM_RAZZBERRY
        maximum_ball = ITEM_GREATBALL if ball_count[
            ITEM_ULTRABALL] < min_ultraball_to_keep else ITEM_ULTRABALL
        ideal_catch_rate_before_throw = self.vip_berry_threshold if is_vip else self.berry_threshold

        used_berry = False
        original_catch_rate_by_ball = catch_rate_by_ball
        while True:

            # find lowest available ball
            current_ball = ITEM_POKEBALL
            while ball_count[current_ball] == 0 and current_ball < maximum_ball:
                current_ball += 1
            if ball_count[current_ball] == 0:
                self.emit_event('no_pokeballs',
                                formatted='No pokeballs left! Fleeing...')
                return WorkerResult.ERROR

            # check future ball count
            num_next_balls = 0
            next_ball = current_ball
            while next_ball < maximum_ball:
                next_ball += 1
                num_next_balls += ball_count[next_ball]

            # check if we've got berries to spare
            berries_to_spare = berry_count > 0 if is_vip else berry_count > num_next_balls + 30

            # use a berry if we are under our ideal rate and have berries to spare
            changed_ball = False
            if catch_rate_by_ball[
                    current_ball] < ideal_catch_rate_before_throw and berries_to_spare and not used_berry:
                new_catch_rate_by_ball = self._use_berry(
                    berry_id, berry_count, encounter_id, catch_rate_by_ball,
                    current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # pick the best ball to catch with
            best_ball = current_ball
            while best_ball < maximum_ball:
                best_ball += 1
                if catch_rate_by_ball[
                        current_ball] < ideal_catch_rate_before_throw and ball_count[
                            best_ball] > 0:
                    # if current ball chance to catch is under our ideal rate, and player has better ball - then use it
                    current_ball = best_ball
                    changed_ball = True

            # if the rate is still low and we didn't throw a berry before, throw one
            if catch_rate_by_ball[
                    current_ball] < ideal_catch_rate_before_throw and berry_count > 0 and not used_berry:
                new_catch_rate_by_ball = self._use_berry(
                    berry_id, berry_count, encounter_id, catch_rate_by_ball,
                    current_ball)
                if new_catch_rate_by_ball != catch_rate_by_ball:
                    catch_rate_by_ball = new_catch_rate_by_ball
                    self.inventory.get(ITEM_RAZZBERRY).remove(1)
                    berry_count -= 1
                    used_berry = True

            # If we change ball then wait to simulate user selecting it
            if changed_ball:
                action_delay(self.catchsim_changeball_wait_min,
                             self.catchsim_changeball_wait_max)

            # Randomize the quality of the throw
            # Default structure
            throw_parameters = {
                'normalized_reticle_size': 1.950,
                'spin_modifier': 1.0,
                'normalized_hit_position': 1.0,
                'throw_type_label': 'Excellent'
            }
            self.generate_spin_parameter(throw_parameters)
            self.generate_throw_quality_parameters(throw_parameters)

            # try to catch pokemon!
            ball_count[current_ball] -= 1
            self.inventory.get(current_ball).remove(1)
            # Take some time to throw the ball from config options
            action_delay(self.catchsim_catch_wait_min,
                         self.catchsim_catch_wait_max)
            self.emit_event(
                'threw_pokeball',
                formatted=
                '{throw_type}{spin_label} throw! Used {ball_name}, with chance {success_percentage} ({count_left} left)',
                data={
                    'throw_type':
                    throw_parameters['throw_type_label'],
                    'spin_label':
                    throw_parameters['spin_label'],
                    'ball_name':
                    self.inventory.get(current_ball).name,
                    'success_percentage':
                    self._pct(catch_rate_by_ball[current_ball]),
                    'count_left':
                    ball_count[current_ball]
                })

            hit_pokemon = 1
            if random() >= self.catch_throw_parameters_hit_rate and not is_vip:
                hit_pokemon = 0

            response_dict = self.bot.api.catch_pokemon(
                encounter_id=encounter_id,
                pokeball=current_ball,
                normalized_reticle_size=throw_parameters[
                    'normalized_reticle_size'],
                spawn_point_id=self.spawn_point_guid,
                hit_pokemon=hit_pokemon,
                spin_modifier=throw_parameters['spin_modifier'],
                normalized_hit_position=throw_parameters[
                    'normalized_hit_position'])

            try:
                catch_pokemon_status = response_dict['responses'][
                    'CATCH_POKEMON']['status']
            except KeyError:
                break

            # retry failed pokemon
            if catch_pokemon_status == CATCH_STATUS_FAILED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted='{pokemon} capture failed.. trying again!',
                    data={'pokemon': pokemon.name})
                used_berry = False
                catch_rate_by_ball = original_catch_rate_by_ball

                # sleep according to flee_count and flee_duration config settings
                # randomly chooses a number of times to 'show' wobble animation between 1 and flee_count
                # multiplies this by flee_duration to get total sleep
                if self.catchsim_flee_count:
                    sleep((randrange(self.catchsim_flee_count) + 1) *
                          self.catchsim_flee_duration)

                continue

            # abandon if pokemon vanished
            elif catch_pokemon_status == CATCH_STATUS_VANISHED:
                #insert into DB
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='vanish_log'"
                    )
                result = c.fetchone()

                while True:
                    if result[0] == 1:
                        conn.execute(
                            '''INSERT INTO vanish_log (pokemon, cp, iv, encounter_id, pokemon_id) VALUES (?, ?, ?, ?, ?)''',
                            (pokemon.name, pokemon.cp, pokemon.iv,
                             str(encounter_id), pokemon.pokemon_id))
                    break
                else:
                    self.emit_event(
                        'vanish_log',
                        sender=self,
                        level='info',
                        formatted="vanish_log table not found, skipping log")
                    break

                self.emit_event('pokemon_vanished',
                                formatted='{} vanished!'.format(pokemon.name),
                                data={
                                    'pokemon': pokemon.name,
                                    'encounter_id':
                                    self.pokemon['encounter_id'],
                                    'latitude': self.pokemon['latitude'],
                                    'longitude': self.pokemon['longitude'],
                                    'pokemon_id': pokemon.pokemon_id
                                })

                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT DISTINCT COUNT(encounter_id) FROM vanish_log WHERE dated > (SELECT dated FROM catch_log WHERE dated IN (SELECT MAX(dated) FROM catch_log))"
                    )

                result = c.fetchone()
                self.consecutive_vanishes_so_far = result[0]

                if self.rest_completed == False and self.consecutive_vanishes_so_far >= self.consecutive_vanish_limit:
                    self.start_rest()

                if self._pct(catch_rate_by_ball[current_ball]) == 100:
                    self.bot.softban = True

            # pokemon caught!
            elif catch_pokemon_status == CATCH_STATUS_SUCCESS:
                if self.rest_completed == True:
                    self.rest_completed = False
                pokemon.unique_id = response_dict['responses'][
                    'CATCH_POKEMON']['captured_pokemon_id']
                self.bot.metrics.captured_pokemon(pokemon.name, pokemon.cp,
                                                  pokemon.iv_display,
                                                  pokemon.iv)

                awards = response_dict['responses']['CATCH_POKEMON'][
                    'capture_award']
                exp_gain, candy_gain, stardust_gain = self.extract_award(
                    awards)
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT DISTINCT COUNT(encounter_id) FROM catch_log WHERE dated >= datetime('now','-1 day')"
                    )

                result = c.fetchone()

                if is_vip:
                    self.emit_event(
                        'pokemon_vip_caught',
                        formatted=
                        'Vip Captured {pokemon}! (CP: {cp} IV: {iv} {iv_display} NCP: {ncp}) Catch Limit: ({caught_last_24_hour}/{daily_catch_limit}) +{exp} exp +{stardust} stardust',
                        data={
                            'pokemon': pokemon.name,
                            'ncp': str(round(pokemon.cp_percent, 2)),
                            'cp': str(int(pokemon.cp)),
                            'iv': str(pokemon.iv),
                            'iv_display': str(pokemon.iv_display),
                            'exp': str(exp_gain),
                            'stardust': stardust_gain,
                            'encounter_id': str(self.pokemon['encounter_id']),
                            'latitude': str(self.pokemon['latitude']),
                            'longitude': str(self.pokemon['longitude']),
                            'pokemon_id': str(pokemon.pokemon_id),
                            'caught_last_24_hour': str(result[0]),
                            'daily_catch_limit': str(self.daily_catch_limit)
                        })

                else:
                    self.emit_event(
                        'pokemon_caught',
                        formatted=
                        'Captured {pokemon}! (CP: {cp} IV: {iv} {iv_display} NCP: {ncp}) Catch Limit: ({caught_last_24_hour}/{daily_catch_limit}) +{exp} exp +{stardust} stardust',
                        data={
                            'pokemon': pokemon.name,
                            'ncp': str(round(pokemon.cp_percent, 2)),
                            'cp': str(int(pokemon.cp)),
                            'iv': str(pokemon.iv),
                            'iv_display': str(pokemon.iv_display),
                            'exp': str(exp_gain),
                            'stardust': stardust_gain,
                            'encounter_id': str(self.pokemon['encounter_id']),
                            'latitude': str(self.pokemon['latitude']),
                            'longitude': str(self.pokemon['longitude']),
                            'pokemon_id': str(pokemon.pokemon_id),
                            'caught_last_24_hour': str(result[0]),
                            'daily_catch_limit': str(self.daily_catch_limit)
                        })

                inventory.pokemons().add(pokemon)
                inventory.player().exp += exp_gain
                self.bot.stardust += stardust_gain
                candy = inventory.candies().get(pokemon.pokemon_id)
                candy.add(candy_gain)

                self.emit_event(
                    'gained_candy',
                    formatted='You now have {quantity} {type} candy!',
                    data={
                        'quantity': candy.quantity,
                        'type': candy.type,
                    },
                )

                self.bot.softban = False

                try:
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute(
                            "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='catch_log'"
                        )
                    result = c.fetchone()

                    while True:
                        if result[0] == 1:
                            conn.execute(
                                '''INSERT INTO catch_log (pokemon, cp, iv, encounter_id, pokemon_id) VALUES (?, ?, ?, ?, ?)''',
                                (pokemon.name, pokemon.cp, pokemon.iv,
                                 str(encounter_id), pokemon.pokemon_id))
                        break
                    else:
                        self.emit_event(
                            'catch_log',
                            sender=self,
                            level='info',
                            formatted="catch_log table not found, skipping log"
                        )
                        break
                    user_data_caught = os.path.join(
                        _base_dir, 'data',
                        'caught-%s.json' % self.bot.config.username)
                    with open(user_data_caught, 'ab') as outfile:
                        json.dump(
                            OrderedDict({
                                'datetime':
                                str(datetime.now()),
                                'pokemon':
                                pokemon.name,
                                'cp':
                                pokemon.cp,
                                'iv':
                                pokemon.iv,
                                'encounter_id':
                                self.pokemon['encounter_id'],
                                'pokemon_id':
                                pokemon.pokemon_id,
                                'latitude':
                                self.pokemon['latitude'],
                                'longitude':
                                self.pokemon['longitude']
                            }), outfile)
                        outfile.write('\n')

                    # if it is a new pokemon to our dex, simulate app animation delay
                    if exp_gain >= 500:
                        sleep(
                            randrange(self.catchsim_newtodex_wait_min,
                                      self.catchsim_newtodex_wait_max))

                except IOError as e:
                    self.logger.info(
                        '[x] Error while opening location file: %s' % e)

            elif catch_pokemon_status == CATCH_STATUS_MISSED:
                self.emit_event(
                    'pokemon_capture_failed',
                    formatted=
                    'Pokeball thrown to {pokemon} missed.. trying again!',
                    data={'pokemon': pokemon.name})
                # Take some time to throw the ball from config options
                action_delay(self.catchsim_catch_wait_min,
                             self.catchsim_catch_wait_max)
                continue

            break
    def work(self):
        forts = self.get_forts_in_range()

        with self.bot.database as conn:
            c = conn.cursor()
            c.execute(
                "SELECT DISTINCT COUNT(pokestop) FROM pokestop_log WHERE dated >= datetime('now','-1 day')"
            )
        if c.fetchone()[0] >= self.config.get('daily_spin_limit', 2000):
            if self.exit_on_limit_reached:
                self.emit_event(
                    'spin_limit',
                    formatted='WARNING! You have reached your daily spin limit'
                )
                sys.exit(2)

            if datetime.now() >= self.next_update:
                self.emit_event(
                    'spin_limit',
                    formatted='WARNING! You have reached your daily spin limit'
                )
                self._compute_next_update()
                return WorkerResult.SUCCESS

        if not self.should_run() or len(forts) == 0:
            return WorkerResult.SUCCESS

        fort = forts[0]

        if fort['id'] in self.streak_forts:
            self.fort_spins = 1
            self.streak_forts = [fort['id']]
        elif self.fort_spins >= 10:
            self.fort_spins = 1
            self.streak_forts = [fort['id']]
        else:
            self.fort_spins += 1

        lat = fort['latitude']
        lng = fort['longitude']

        details = fort_details(self.bot, fort['id'], lat, lng)

        fort_name = details.get('name', 'Unknown')
        check_fort_modifier = details.get('modifiers', {})
        if self.use_lure and check_fort_modifier:
            # check_fort_modifier_id = check_fort_modifier[0].get('item_id')
            self.emit_event(
                'lure_info',
                formatted='A lure is already in fort, skip deploying lure')

        if self.use_lure and not check_fort_modifier:
            # check lures availiblity
            lure_count = inventory.items().get(501).count

            if lure_count > 1:  # Only use lures when there's more than one
                request = self.bot.api.create_request()
                request.add_fort_modifier(
                    modifier_type=501,
                    fort_id=fort['id'],
                    player_latitude=f2i(self.bot.position[0]),
                    player_longitude=f2i(self.bot.position[1]))
                response_dict = request.call()

                if ('responses'
                        in response_dict) and ('ADD_FORT_MODIFIER'
                                               in response_dict['responses']):
                    add_modifier_deatils = response_dict['responses'][
                        'ADD_FORT_MODIFIER']
                    add_modifier_result = add_modifier_deatils.get(
                        'result', -1)
                    if (add_modifier_result == LURE_REQUEST_RESULT_SUCCESS):
                        self.emit_event(
                            'lure_success',
                            formatted='You have successfully placed a lure')
                    if (add_modifier_result ==
                            LURE_REQUEST_FORT_ALREADY_HAS_MODIFIER):
                        self.emit_event(
                            'lure_failed',
                            formatted=
                            'A lure has being placed before you try to do so')
                    if (add_modifier_result == LURE_REQUEST_TOO_FAR_AWAY):
                        self.emit_event('lure_failed',
                                        formatted='Pokestop out of range')
                    if (add_modifier_result ==
                            LURE_REQUEST_NO_ITEM_IN_INVENTORY):
                        self.emit_event(
                            'lure_not_enough',
                            formatted='Not enough lure in inventory')
                    if (add_modifier_result == LURE_REQUEST_POI_INACCESSIBLE):
                        self.emit_event('lure_info', formatted='Unkown Error')
            else:
                self.emit_event('lure_not_enough',
                                formatted='Not enough lure in inventory')

        request = self.bot.api.create_request()
        request.fort_search(fort_id=fort['id'],
                            fort_latitude=lat,
                            fort_longitude=lng,
                            player_latitude=f2i(self.bot.position[0]),
                            player_longitude=f2i(self.bot.position[1]))
        response_dict = request.call()

        if ('responses' in response_dict) and ('FORT_SEARCH'
                                               in response_dict['responses']):
            spin_details = response_dict['responses']['FORT_SEARCH']
            spin_result = spin_details.get('result', -1)

            if (spin_result == SPIN_REQUEST_RESULT_SUCCESS) or (
                    spin_result == SPIN_REQUEST_RESULT_INVENTORY_FULL):
                self.bot.softban = False
                experience_awarded = spin_details.get('experience_awarded', 0)
                items_awarded = self.get_items_awarded_from_fort_spinned(
                    response_dict)
                egg_awarded = spin_details.get('pokemon_data_egg', None)
                gym_badge_awarded = spin_details.get('awarded_gym_badge', None)
                chain_hack_sequence_number = spin_details.get(
                    'chain_hack_sequence_number', 0)

                if egg_awarded is not None:
                    items_awarded[u'Egg'] = egg_awarded['egg_km_walked_target']

                # if gym_badge_awarded is not None:
                #     self.logger.info("Gained a Gym Badge! %s" % gym_badge_awarded)
                #
                # if chain_hack_sequence_number > 0:
                #     self.logger.info("Chain hack sequence: %s" % chain_hack_sequence_number)

                if experience_awarded or items_awarded:
                    awards = ', '.join([
                        "{}x {}".format(items_awarded[x], x)
                        for x in items_awarded if x != u'Egg'
                    ])
                    if egg_awarded is not None:
                        awards += u', {} Egg'.format(
                            egg_awarded['egg_km_walked_target'])
                    self.fort_spins = chain_hack_sequence_number

                    if "type" in fort and fort["type"] == 1:
                        # It's a Pokestop
                        stop_kind = "pokestop"
                    else:
                        # It's a gym
                        stop_kind = "gym"

                    self.emit_event(
                        'spun_pokestop',
                        formatted=
                        "Spun {stop_kind} {pokestop} ({spin_amount_now} streak). Experience awarded: {exp}. Items awarded: {items}",
                        data={
                            'stop_kind': stop_kind,
                            'pokestop': fort_name,
                            'exp': experience_awarded,
                            'spin_amount_now': chain_hack_sequence_number,
                            'items': awards
                        })
                    #time.sleep(10)
                else:
                    self.emit_event(
                        'pokestop_empty',
                        formatted='Found nothing in pokestop {pokestop}.',
                        data={'pokestop': fort_name})
                with self.bot.database as conn:
                    c = conn.cursor()
                    c.execute(
                        "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='pokestop_log'"
                    )
                result = c.fetchone()
                while True:
                    if result[0] == 1:
                        conn.execute(
                            '''INSERT INTO pokestop_log (pokestop, exp, items) VALUES (?, ?, ?)''',
                            (fort_name, str(experience_awarded),
                             str(items_awarded)))
                        break
                    else:
                        self.emit_event(
                            'pokestop_log',
                            sender=self,
                            level='info',
                            formatted=
                            "pokestop_log table not found, skipping log")
                        break
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                self.bot.fort_timeouts.update({fort["id"]: pokestop_cooldown})
                self.bot.recent_forts = self.bot.recent_forts[1:] + [
                    fort['id']
                ]
            elif spin_result == SPIN_REQUEST_RESULT_OUT_OF_RANGE:
                self.emit_event('pokestop_out_of_range',
                                formatted="Pokestop {pokestop} out of range.",
                                data={'pokestop': fort_name})
            elif spin_result == SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD:
                pokestop_cooldown = spin_details.get(
                    'cooldown_complete_timestamp_ms')
                if pokestop_cooldown:
                    self.bot.fort_timeouts.update(
                        {fort["id"]: pokestop_cooldown})
                    seconds_since_epoch = time.time()
                    minutes_left = format_time((pokestop_cooldown / 1000) -
                                               seconds_since_epoch)
                    self.emit_event(
                        'pokestop_on_cooldown',
                        formatted=
                        "Pokestop {pokestop} on cooldown. Time left: {minutes_left}.",
                        data={
                            'pokestop': fort_name,
                            'minutes_left': minutes_left
                        })
            elif spin_result == SPIN_REQUEST_RESULT_POI_INACCESSIBLE:
                self.logger.info("Pokestop not accessable at this time.")
                self.bot.fort_timeouts[fort["id"]] = (
                    time.time() + 300) * 1000  # Don't spin for 5m
            else:
                self.emit_event('unknown_spin_result',
                                formatted="Unknown spint result {status_code}",
                                data={'status_code': str(spin_result)})
            if 'chain_hack_sequence_number' in response_dict['responses'][
                    'FORT_SEARCH']:
                action_delay(self.spin_wait_min, self.spin_wait_max)
                return response_dict['responses']['FORT_SEARCH'][
                    'chain_hack_sequence_number']
            else:
                self.emit_event(
                    'pokestop_searching_too_often',
                    formatted="Possibly searching too often, take a rest.")
                if spin_result == 1 and not items_awarded and not experience_awarded and not pokestop_cooldown:
                    self.bot.softban = True
                    self.emit_event('softban',
                                    formatted='Probably got softban.')
                    with self.bot.database as conn:
                        c = conn.cursor()
                        c.execute(
                            "SELECT COUNT(name) FROM sqlite_master WHERE type='table' AND name='softban_log'"
                        )
                    result = c.fetchone()

                    if result[0] == 1:
                        source = str("SpinFort")
                        status = str("Possible Softban")
                        conn.execute(
                            '''INSERT INTO softban_log (status, source) VALUES (?, ?)''',
                            (status, source))
                    else:
                        self.emit_event(
                            'softban_log',
                            sender=self,
                            level='info',
                            formatted=
                            "softban_log table not found, skipping log")

                self.bot.fort_timeouts[fort["id"]] = (
                    time.time() + 300) * 1000  # Don't spin for 5m

                return WorkerResult.ERROR
        action_delay(self.spin_wait_min, self.spin_wait_max)

        if len(forts) > 1:
            return WorkerResult.RUNNING

        return WorkerResult.SUCCESS