From 8d3d5f83d6f8437c19971914e8eb81a7a2b6af37 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 12:34:12 +0200 Subject: [PATCH 01/10] Inbox call added to heartbeat This will return the notifications send by the server and display them if its a new notification --- pokemongo_bot/__init__.py | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index b19ac28cf0..ac3d5b8726 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -72,6 +72,15 @@ def player_data(self): """ return self._player + @property + def inbox(self): + """ + Returns the inbox data as received from the API. + :return: The inbox data. + :rtype: dict + """ + return self._inbox + @property def stardust(self): dust = filter(lambda y: y['name'] == 'STARDUST', self._player['currencies'])[0] @@ -777,6 +786,16 @@ def _register_events(self): self.event_manager.register_event('catch_limit_on') self.event_manager.register_event('catch_limit_off') + self.event_manager.register_event( + 'pokemon_knock_out_gym', + parameters=('pokemon', 'gym_name', 'notification_date', 'awarded_coins', 'awarded_coins_today') + ) + + self.event_manager.register_event( + 'pokemon_hungy', + parameters=('pokemon', 'gym_name', 'notification_date') + ) + def tick(self): self.health_record.heartbeat() @@ -1548,10 +1567,13 @@ def heartbeat(self): if timeout >= now * 1000} if now - self.last_heartbeat >= self.heartbeat_threshold and not self.hb_locked: + previous_heartbeat = self.last_heartbeat self.last_heartbeat = now request = self.api.create_request() request.get_player() request.check_awarded_badges() + request.get_inbox() + responses = None try: responses = request.call() except NotLoggedInException: @@ -1572,6 +1594,64 @@ def heartbeat(self): formatted='player_data: {player_data}', data={'player_data': self._player} ) + if responses['responses']['GET_INBOX']['result'] == 1: + self._inbox = responses['responses']['GET_INBOX']['inbox'] + # self.logger.info("Got inbox messages?") + # self.logger.info("Inbox: %s" % responses['responses']['GET_INBOX']) + if 'notifications' in self._inbox: + for notification in self._inbox['notifications']: + notification_date = datetime.datetime.fromtimestamp(int(notification['create_timestamp_ms']) / 1e3) + if previous_heartbeat > (int(notification['create_timestamp_ms']) / 1e3): + # Skipp old notifications! + continue + + if notification['category'] == 'pokemon_hungry': + gym_name = pokemon = 'Unknown' + for variable in notification['variables']: + if variable['name'] == 'GYM_NAME': + gym_name = variable['literal'] + if variable['name'] == 'POKEMON_NICKNAME': + pokemon = variable['literal'] + + self.event_manager.emit( + 'pokemon_hungy', + sender=self, + level='info', + formatted='{pokemon} in the Gym {gym_name} is hungy and want a candy! {notification_date}', + data={ + 'pokemon': pokemon, + 'gym_name': gym_name, + 'notification_date': notification_date.strftime('%Y-%m-%d %H:%M:%S.%f') + } + ) + + if notification['category'] == 'gym_removal': + gym_name = pokemon = 'Unknown' + for variable in notification['variables']: + if variable['name'] == 'GYM_NAME': + gym_name = variable['literal'] + if variable['name'] == 'POKEMON_NICKNAME': + pokemon = variable['literal'] + if variable['name'] == 'POKECOIN_AWARDED': + coins_awared = variable['literal'] + if variable['name'] == 'POKECOIN_AWARDED_TODAY': + coins_awared_today = variable['literal'] + + self.event_manager.emit( + 'pokemon_knock_out_gym', + sender=self, + level='info', + formatted='{pokemon} has been knocked out the Gym {gym_name} at {notification_date}. Awarded coins: {awarded_coins} | Today awared: {awarded_coins_today}', + data={ + 'pokemon': pokemon, + 'gym_name': gym_name, + 'notification_date': notification_date.strftime('%Y-%m-%d %H:%M:%S.%f'), + 'awarded_coins': coins_awared, + 'awarded_coins_today': coins_awared_today + } + ) + + if responses['responses']['CHECK_AWARDED_BADGES']['success'] == True: # store awarded_badges reponse to be used in a task or part of heartbeat self._awarded_badges = responses['responses']['CHECK_AWARDED_BADGES'] From d6a197aeeb5a983d94d67b42afe98232a3b8b323 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 12:43:44 +0200 Subject: [PATCH 02/10] BadPokemon worker If you have any Pokemon that Niantic has marked as bad (red slashes) this will notify you. If you set the option, it will also transfer those Pokemon. --- docs/configuration_files.md | 31 +++++ pokemongo_bot/cell_workers/__init__.py | 3 +- pokemongo_bot/cell_workers/bad_pokemon.py | 141 ++++++++++++++++++++++ 3 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 pokemongo_bot/cell_workers/bad_pokemon.py diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 48d798bcfd..e44d0cf954 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -62,6 +62,7 @@ - [CompleteTutorial](#completetutorial) - [BuddyPokemon](#buddypokemon) - [PokemonHunter](#pokemonhunter) +- [BadPokemon](#badpokemon) # Configuration files @@ -1448,3 +1449,33 @@ Also hunting for trash does NOT lock the target, catching all Pokemon it find on } } ``` +## BadPokemon +[[back to top](#table-of-contents)] + +### Description +[[back to top](#table-of-contents)] + +If you have any Pokemon that Niantic has marked as bad (red slashes) this will notify you. If you set the option, it will also transfer those Pokemon. + +### Options +[[back to top](#table-of-contents)] + +* `transfer`: `Default: False`. Must we transfer the bad Pokemon? +* `bulktransfer_enabled`: `Default: True`. If we do transfer the bad Pokemon, may we do so in a batch? +* `action_wait_min`: `Default: 3`. Wait time min +* `action_wait_min`: `Default: 5`. Wait time max +* `min_interval`: `Default: 120`. Run once every X seconds + + +### Sample configuration +[[back to top](#table-of-contents)] +```json +{ + "type": "BadPokemon", + "config": { + "enabled": true, + "transfer": true, + "min_interval": 240 + } +} +``` diff --git a/pokemongo_bot/cell_workers/__init__.py b/pokemongo_bot/cell_workers/__init__.py index 551a3441ce..a08e45da4a 100644 --- a/pokemongo_bot/cell_workers/__init__.py +++ b/pokemongo_bot/cell_workers/__init__.py @@ -32,4 +32,5 @@ from .discord_task import DiscordTask from .buddy_pokemon import BuddyPokemon from .catch_limiter import CatchLimiter -from .update_hash_stats import UpdateHashStats \ No newline at end of file +from .update_hash_stats import UpdateHashStats +from .bad_pokemon import BadPokemon diff --git a/pokemongo_bot/cell_workers/bad_pokemon.py b/pokemongo_bot/cell_workers/bad_pokemon.py new file mode 100644 index 0000000000..e915d3e19f --- /dev/null +++ b/pokemongo_bot/cell_workers/bad_pokemon.py @@ -0,0 +1,141 @@ +from pokemongo_bot import inventory +from pokemongo_bot.inventory import Pokemon +from pokemongo_bot.inventory import Pokemons +from pokemongo_bot.human_behaviour import sleep, action_delay +from pokemongo_bot.base_task import BaseTask +from pokemongo_bot.worker_result import WorkerResult +from datetime import datetime, timedelta + +class BadPokemon(BaseTask): + SUPPORTED_TASK_API_VERSION = 1 + + def __init__(self, bot, config): + super(BadPokemon, self).__init__(bot, config) + + def initialize(self): + self.config_transfer = self.config.get('transfer', False) + self.config_bulktransfer_enabled = self.config.get('bulktransfer_enabled', True) + self.config_action_wait_min = self.config.get("action_wait_min", 3) + self.config_action_wait_max = self.config.get("action_wait_max", 5) + self.min_interval = self.config.get('min_interval', 120) + self.next_update = None + + def work(self): + bad_pokemons = [p for p in inventory.pokemons().all() if p.is_bad] + + if len(bad_pokemons) > 0: + if self._should_print(): + self.logger.warning("You have %s bad (slashed) Pokemon!" % len(bad_pokemons)) + self._compute_next_update() + sleep(1) + if self.config_transfer: + self.transfer_pokemon(bad_pokemons) + + return WorkerResult.SUCCESS + + def _should_print(self): + return self.next_update is None or datetime.now() >= self.next_update + + def _compute_next_update(self): + self.next_update = datetime.now() + timedelta(seconds=self.min_interval) + + 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 + + From abcc0f87394afc4c489fcfeed354fedba1d60ca1 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 12:54:45 +0200 Subject: [PATCH 03/10] Pokemon Hunter changes --- pokemongo_bot/__init__.py | 7 + pokemongo_bot/cell_workers/pokemon_hunter.py | 397 +++++++++++++++++-- pokemongo_bot/event_manager.py | 2 +- pokemongo_bot/walkers/step_walker.py | 3 + 4 files changed, 364 insertions(+), 45 deletions(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index b19ac28cf0..e6cd68b5e6 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -352,6 +352,13 @@ def _register_events(self): 'longitude' ) ) + self.event_manager.register_event( + 'moving_to_hunter_target', + parameters=( + 'target_name', + 'distance' + ) + ) self.event_manager.register_event( 'moving_to_fort', parameters=( diff --git a/pokemongo_bot/cell_workers/pokemon_hunter.py b/pokemongo_bot/cell_workers/pokemon_hunter.py index 16abfeea48..8d3ccff6cc 100644 --- a/pokemongo_bot/cell_workers/pokemon_hunter.py +++ b/pokemongo_bot/cell_workers/pokemon_hunter.py @@ -14,13 +14,14 @@ from pokemongo_bot.walkers.polyline_walker import PolylineWalker from pokemongo_bot.walkers.step_walker import StepWalker from pokemongo_bot.worker_result import WorkerResult -from .utils import fort_details +from .utils import fort_details, format_dist, distance import random from random import uniform class PokemonHunter(BaseTask): SUPPORTED_TASK_API_VERSION = 1 + LOOK_AROUND_TIME = 20 def __init__(self, bot, config): super(PokemonHunter, self).__init__(bot, config) @@ -29,15 +30,19 @@ def initialize(self): self.max_pokemon_storage = inventory.get_pokemon_inventory_size() self.notified_second_gen = [] self.destination = None + self.previous_destination = None self.walker = None self.search_cell_id = None self.search_points = [] self.lost_counter = 0 + self.lost_map_counter = 0 self.no_log_until = 0 self.distance_to_target = 0 self.distance_counter = 0 self.recent_tries = [] - self.no_hunt_until = None + # No hunting from the start; give sightings a few secs to load! + self.no_hunt_until = time.time() + 10 + self.no_look_around_until = time.time() + 20 self.hunt_started_at = None self.config_max_distance = self.config.get("max_distance", 2000) @@ -62,6 +67,9 @@ def initialize(self): self.config_hunt_for_trash = self.config.get("hunt_for_trash_to_fill_bag", False) self.config_trash_hunt_open_slots = self.config.get("trash_hunt_open_slots", 25) self.hunting_trash = False + # Allow the bot to run to a VIP? + self.config_run_to_vip = self.config.get("run_to_vip", False) + self.runs_to_vips = 0 def work(self): if not self.enabled: @@ -115,12 +123,22 @@ def work(self): self.hunting_trash = False return WorkerResult.SUCCESS + if hasattr(self.bot,"hunter_locked_target"): + if self.destination is not None and self.bot.hunter_locked_target is not None: + if self.destination is not self.bot.hunter_locked_target: + self.logger.info("Locked on to a different target than destination??") + self.bot.hunter_locked_target = None + if self.destination is not None: if self.destination_caught(): self.logger.info("We found a %(name)s while hunting.", self.destination) - self.recent_tries.append(self.destination['pokemon_id']) + # self.recent_tries.append(self.destination['pokemon_id']) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_counter = 0 + self.lost_map_counter = 0 if self.config_enable_cooldown: wait = uniform(120, 600) self.no_hunt_until = time.time() + wait @@ -131,9 +149,13 @@ def work(self): if self.destination_vanished(): self.logger.info("Darn, target got away!") - self.recent_tries.append(self.destination['pokemon_id']) + # self.recent_tries.append(self.destination['pokemon_id']) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_counter = 0 + self.lost_map_counter = 0 if self.config_enable_cooldown: wait = uniform(120, 600) self.no_hunt_until = time.time() + wait @@ -144,30 +166,92 @@ def work(self): now = time.time() pokemons = self.get_nearby_pokemons() + pokemons = filter(lambda x: x["pokemon_id"] not in self.recent_tries, pokemons) + trash_mons = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] - if self.config_hunt_for_trash and self.hunting_trash is False and (self.destination is None or self._is_vip_pokemon(self.destination) is False ): + if self.destination is not None: + target_mons = filter(lambda x: x["name"] is self.destination["name"], pokemons) + if self.no_log_until < now: + # self.logger.info("Targets on sightings: %s" % len(target_mons)) + if len(pokemons) > 0: + if len(target_mons) < 1: + # Target off sightings; must be getting close + # Drops of at about 120 meters to target... + distance = great_circle(self.bot.position, (self.walker.dest_lat, self.walker.dest_lng)).meters + if (distance > 125 and self.lost_map_counter > 4) or self.lost_map_counter > 10: + # If > 120 meter => must be gone? + # Searching for 10 times, give up... + self.logger.info("It seems %(name)s is no longer there!", self.destination) + self.destination = None + self.hunting_trash = False + self.bot.hunter_locked_target = None + self.lost_map_counter = 0 + self.lost_counter = 0 + if self.config_enable_cooldown: + wait = uniform(120, 600) + self.no_hunt_until = time.time() + wait + self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) + return WorkerResult.SUCCESS + else: + self.logger.info("Electing new target....") + else: + self.lost_map_counter += 1 + else: + self.lost_map_counter = 0 + else: + self.logger.info("No sightings available at the moment...") + + if self.config_hunt_for_trash and self.hunting_trash is False and (self.destination is None or not self._is_vip_pokemon(self.destination) ): # Okay, we should hunt for trash if the bag is almost full - trash_mons = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] + pokemons.sort(key=lambda p: p["distance"]) + possible_targets = filter(lambda x: x["name"] in trash_mons, pokemons) if self.pokemon_slots_left() <= self.config_trash_hunt_open_slots: if self.no_log_until < now: - self.logger.info("Less than %s slots left to fill, starting hunt for trash" % elf.config_trash_hunt_open_slots) - for pokemon in pokemons: - if pokemon["name"] in trash_mons: - self.hunting_trash = True - self.destination = pokemon - self.lost_counter = 0 - self.hunt_started_at = datetime.now() - self.logger.info("Hunting for trash at %(distance).2f meters: %(name)s", self.destination) - self.set_target() - # We have a target - return WorkerResult.SUCCESS + self.logger.info("Less than %s slots left to fill, starting hunt for trash" % self.config_trash_hunt_open_slots) + if len(possible_targets) is 0: + self.logger.info("No trash pokemon around...") + for pokemon in possible_targets: + if self.destination is not None: + self.logger.info("Trash hunt takes priority! Changing target...") + self.hunting_trash = True + self.destination = pokemon + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("Hunting for trash at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING if self.config_hunt_for_trash and self.hunting_trash: - if self.pokemon_slots_left() > 20: + if self.pokemon_slots_left() > self.config_trash_hunt_open_slots: self.logger.info("No longer trying to fill the bag. Electing new target....") self.hunting_trash = False self.destination = None + # Closer target? + if self.no_log_until < now: + # Don't check every tick! + if self.destination is not None and len(pokemons) > 0: + pokemons.sort(key=lambda p: p["distance"]) + possible_targets = filter(lambda x: x["name"] in trash_mons, pokemons) + # Check for a closer target + self.destination["distance"] = self.get_distance(self.bot.position, self.destination) + for pokemon in possible_targets: + if pokemon is not self.destination: + if round(pokemon["distance"], 2) >= round(self.destination["distance"], 2): + # further away! + break + self.logger.info("Found a closer target: %s < %s" % (pokemon["distance"], self.destination["distance"])) + if self.destination is not None: + self.logger.info("Closer trash hunt takes priority! Changing target...") + self.hunting_trash = True + self.destination = pokemon + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING if self.destination is None: worth_pokemons = self.get_worth_pokemons(pokemons, self.config_hunt_closest_first) @@ -175,19 +259,48 @@ def work(self): if len(worth_pokemons) > 0: # Pick a random target from the list # random.shuffle(worth_pokemons) - # Priotize closer pokemon - worth_pokemons.sort(key=lambda p: p["distance"]) + if self.config_hunt_closest_first: + # Priotize closer pokemon + worth_pokemons.sort(key=lambda p: p["distance"]) + else: + random.shuffle(worth_pokemons) # Prevents the bot from looping the same Pokemon self.destination = worth_pokemons[0] + + if self.previous_destination is not None: + # Check if we are hunting the same target again... + if self.previous_destination["pokemon_id"] == self.destination["pokemon_id"]: + # Hunting the same pokemon again? + if "fort_id" in self.previous_destination and "fort_id" in self.destination and \ + self.previous_destination["fort_id"] == self.destination["fort_id"]: + # Hunting the same target again? + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + else: + # Checking if it's the same distance... + self.previous_destination["distance"] = self.get_distance(self.bot.start_position, self.previous_destination) + self.destination["distance"] = self.get_distance(self.bot.start_position, self.destination) + if round(self.previous_destination["distance"], 2) == round(self.destination["distance"], 2): + self.logger.info("Likely we are trying the same Pokemon again") + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + + if self.previous_destination == self.destination: + # We already caught that Pokemon! + if len(worth_pokemons) > 1: + self.destination = worth_pokemons[1] + self.set_target() self.lost_counter = 0 self.hunt_started_at = datetime.now() self.logger.info("New destination at %(distance).2f meters: %(name)s", self.destination) - if self._is_vip_pokemon(self.destination): - self.logger.info("This is a VIP Pokemon! Starting hunt.") - if self.config_lock_on_target: - self.bot.hunter_locked_target = self.destination + if self._is_vip_pokemon(self.destination) and self.config_lock_on_target: + self.logger.info("This is a VIP Pokemon! Locking on to target!") + self.bot.hunter_locked_target = self.destination + elif self._is_family_of_vip(self.destination) and self.config_treat_family_of_vip_as_vip and self.config_lock_on_target: + self.logger.info("This Pokemon is family of a VIP! Locking target!") + self.bot.hunter_locked_target = self.destination elif self._is_needed_pokedex(self.destination): self.logger.info("I need a %(name)s to complete the Pokedex! I have %(candies)s candies.", self.destination) if self.config_lock_on_target and not self.config_lock_vip_only: @@ -203,8 +316,10 @@ def work(self): # Show like "Pidgey (12), Zubat(2)" names = Counter((p["name"] for p in pokemons)) sorted(names) # unicode object, no lower? , key=str.lower) - - self.logger.info("There is no nearby pokemon worth hunting down [%s]", ", ".join('{}({})'.format(key, val) for key, val in names.items())) + if len(names) > 0: + self.logger.info("There is no nearby pokemon worth hunting down [%s]", ", ".join('{}({})'.format(key, val) for key, val in names.items())) + else: + self.logger.info("No sightings available at the moment...") self.no_log_until = now + 120 self.destination = None if self.config_enable_cooldown: @@ -217,9 +332,79 @@ def work(self): return WorkerResult.SUCCESS # Make sure a VIP is treated that way - if self.config_lock_on_target and self.destination is not None: - if self._is_vip_pokemon(self.destination) and self.bot.hunter_locked_target is None: + if self.config_lock_on_target and self.bot.hunter_locked_target is None and self.destination is not None: + if self._is_vip_pokemon(self.destination): self.bot.hunter_locked_target = self.destination + #Check if we are treating Family of VIP as VIP + if self.config_treat_family_of_vip_as_vip and self.destination is not None: + if self._is_family_of_vip(self.destination): + # We're hunting for family, so we need to check if we find a VIP + if self.no_log_until < now: + # Not every tick please + possible_targets = filter(lambda p: self._is_vip_pokemon(p), pokemons) + # Update the distance to targets + for p in possible_targets: + p["distance"] = self.get_distance(self.bot.position, p) + possible_targets.sort(key=lambda p: p["distance"]) + if len(possible_targets) > 0: + # Check if it's not the same mon... + if possible_targets[0]["name"] != self.destination["name"]: + self.logger.info("We found a real VIP while hunting for %(name)s", self.destination) + self.destination = possible_targets[0] + self.bot.hunter_locked_target = self.destination + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New VIP target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING + + # Now we check if there is a VIP target closer by + if self.destination is not None and self.bot.hunter_locked_target is self.destination: + # Hunting a VIP, checking for closer VIP target + if self.no_log_until < now: + # Not every tick please + possible_targets = filter(lambda p: self._is_vip_pokemon(p), pokemons) + # Update the distance to targets + for p in possible_targets: + p["distance"] = self.get_distance(self.bot.position, p) + possible_targets.sort(key=lambda p: p["distance"]) + # Check for a closer target + self.destination["distance"] = self.get_distance(self.bot.position, self.destination) + for pokemon in possible_targets: + if pokemon is not self.destination: + if round(pokemon["distance"], 2) >= round(self.destination["distance"], 2): + # further away! + break + with self.bot.database as conn: + c = conn.cursor() + c.execute( + "SELECT COUNT(pokemon) FROM catch_log where pokemon = '{}' and datetime(dated, 'localtime') > Datetime('{}')".format(pokemon["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S"))) + # Now check if there is 1 or more caught + amount = c.fetchone()[0] + if amount > 0: + # We caught this pokemon recently, skip it + continue + if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): + if self._is_vip_pokemon(self.destination): + self.logger.info("Seeing a familymember of a VIP at %(distance).2f meters: %(name)s", pokemon) + self.logger.info("Not hunting down because we are locked to a real VIP: %(name)s", self.destination) + continue + else: + self.logger.info("Closer (is distance) familymember of VIP found!") + + self.logger.info("Found a closer VIP target: %s < %s" % (pokemon["distance"], self.destination["distance"])) + if self.destination is not None: + self.logger.info("Closer VIP hunt takes priority! Changing target...") + self.destination = pokemon + self.bot.hunter_locked_target = self.destination + self.lost_counter = 0 + self.hunt_started_at = datetime.now() + self.logger.info("New VIP target at %(distance).2f meters: %(name)s", self.destination) + self.set_target() + # We have a target + return WorkerResult.RUNNING + # Check if there is a VIP around to hunt if (self.destination is not None and @@ -238,9 +423,8 @@ def work(self): self.set_target() if self.config_lock_on_target: self.bot.hunter_locked_target = self.destination - self.logger.info("Found a VIP Pokemon! Looking for a %(name)s at %(distance).2f.", self.destination) + self.logger.info("Spotted a VIP Pokemon! Looking for a %(name)s at %(distance).2f.", self.destination) return WorkerResult.SUCCESS - pass if self.destination is None: if self.no_log_until < now: @@ -250,6 +434,7 @@ def work(self): if self.config_lock_on_target and not self.config_lock_vip_only: if self.bot.hunter_locked_target == None: self.logger.info("We found a %(name)s while hunting. Aborting the current search.", self.destination) + self.previous_destination = self.destination self.destination = None self.hunting_trash = False if self.config_enable_cooldown: @@ -258,10 +443,31 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) return WorkerResult.SUCCESS + # Determin if we are allowed to run to a VIP + different_target = False + if self.destination is not None: + if self.previous_destination is None: + self.previous_destination = self.destination + elif self.previous_destination is not self.destination: + different_target = True + self.previous_destination = self.destination + + if self.config_run_to_vip and self._is_vip_pokemon(self.destination): + if self.runs_to_vips > 3: + self.logger.info("Ran to 3 Pokemon in a row. Cooling down...") + self.runs_to_vips = 0 + speed = None + else: + speed = self.bot.config.walk_max + if different_target: + self.runs_to_vips += 1 + else: + speed = None + if any(self.destination["encounter_id"] == p["encounter_id"] for p in self.bot.cell["catchable_pokemons"] + self.bot.cell["wild_pokemons"]): self.destination = None self.hunting_trash = False - elif self.walker.step(): + elif self.walker.step(speed): if not any(self.destination["encounter_id"] == p["encounter_id"] for p in pokemons): self.lost_counter += 1 else: @@ -278,9 +484,11 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) else: self.logger.info("Now searching for %(name)s", self.destination) - - self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) - self.search_points = self.search_points[1:] + self.search_points[:1] + if self.search_points == []: + self.walker = StepWalker(self.bot, self.destination['latitude'], self.destination['longitude']) + else: + self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.search_points = self.search_points[1:] + self.search_points[:1] elif self.no_log_until < now: distance = great_circle(self.bot.position, (self.walker.dest_lat, self.walker.dest_lng)).meters if round(distance, 2) == self.distance_to_target: @@ -289,7 +497,13 @@ def work(self): else: self.distance_counter = 0 - if self.distance_counter >= 3: + if self.distance_counter is 3: + # Try another walker + self.logger.info("Having difficulty walking to target, changing walker!") + self.walker = StepWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.distance_counter += 1 + + if self.distance_counter >= 6: # Ignore last 3 if len(self.recent_tries) > 3: self.recent_tries.pop() @@ -305,13 +519,32 @@ def work(self): self.logger.info("Hunting on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) return WorkerResult.ERROR else: - self.logger.info("Moving to destination at %s meters: %s", round(distance, 2), self.destination["name"]) + unit = self.bot.config.distance_unit # Unit to use when printing formatted distance + if speed is not None: + self.emit_event( + 'moving_to_hunter_target', + formatted="Running towards VIP target {target_name} - {distance}", + data={ + 'target_name': u"{}".format(self.destination["name"]), + 'distance': format_dist(distance, unit), + } + ) + else: + self.emit_event( + 'moving_to_hunter_target', + formatted="Moving towards target {target_name} - {distance}", + data={ + 'target_name': u"{}".format(self.destination["name"]), + 'distance': format_dist(distance, unit), + } + ) + # self.logger.info("Moving to destination at %s meters: %s", round(distance, 2), self.destination["name"]) # record the new distance... self.distance_to_target = round(distance, 2) if self.config_lock_on_target and not self.config_lock_vip_only: # Just to ensure we stay on target self.bot.hunter_locked_target = self.destination - self.no_log_until = now + 30 + self.no_log_until = now + 5 return WorkerResult.RUNNING @@ -319,10 +552,20 @@ def get_pokeball_count(self): return sum([inventory.items().get(ball.value).count for ball in [Item.ITEM_POKE_BALL, Item.ITEM_GREAT_BALL, Item.ITEM_ULTRA_BALL]]) def set_target(self): - self.search_points = self.get_search_points(self.destination["s2_cell_id"]) - self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) - self.search_cell_id = self.destination["s2_cell_id"] - self.search_points = self.search_points[1:] + self.search_points[:1] + if not 's2_cell_id' in self.destination: + # This Pokemon has coords + self.search_points = [] + self.walker = PolylineWalker(self.bot, self.destination["latitude"], self.destination["longitude"]) + self.logger.info("Target must be close by...") + # self.logger.info("destination: %s" % self.destination) + # self.search_points = self.get_search_points(self.bot.cell["s2_cell_id"]) + # self.search_cell_id = self.bot.cell["s2_cell_id"] + # self.search_points = self.search_points[1:] + self.search_points[:1] + else: + self.search_points = self.get_search_points(self.destination["s2_cell_id"]) + self.walker = PolylineWalker(self.bot, self.search_points[0][0], self.search_points[0][1]) + self.search_cell_id = self.destination["s2_cell_id"] + self.search_points = self.search_points[1:] + self.search_points[:1] if "fort_id" in self.destination: # The Pokemon is hding at a POkestop, so move to that Pokestop! @@ -337,6 +580,15 @@ def set_target(self): fort_name = details.get('name', 'Unknown') self.logger.info("%s is hiding at %s, going there first!" % (self.destination["name"], fort_name)) self.walker = PolylineWalker(self.bot, lat, lng) + else: + nearest_fort = self.get_nearest_fort_on_the_way() + if nearest_fort is not None: + lat = nearest_fort['latitude'] + lng = nearest_fort['longitude'] + details = fort_details(self.bot, nearest_fort['id'], lat, lng) + fort_name = details.get('name', 'Unknown') + self.logger.info("Moving to %s via %s." % (self.destination["name"], fort_name)) + self.walker = PolylineWalker(self.bot, lat, lng) def pokemon_slots_left(self): left = self.max_pokemon_storage - inventory.Pokemons.get_space_used() @@ -347,9 +599,39 @@ def get_nearby_pokemons(self): pokemons = [p for p in self.bot.cell["nearby_pokemons"] if self.get_distance(self.bot.start_position, p) <= radius] + if 'wild_pokemons' in self.bot.cell: + for pokemon in self.bot.cell['wild_pokemons']: + if pokemon['encounter_id'] in map(lambda pokemon: pokemon['encounter_id'], pokemons): + # Already added this Pokemon + continue + if self.get_distance(self.bot.start_position, pokemon) <= radius: + pokemons.append(pokemon) + + if 'catchable_pokemons' in self.bot.cell: + for pokemon in self.bot.cell['catchable_pokemons']: + if pokemon['encounter_id'] in map(lambda pokemon: pokemon['encounter_id'], pokemons): + # Already added this Pokemon + continue + if self.get_distance(self.bot.start_position, pokemon) <= radius: + pokemons.append(pokemon) + for pokemon in pokemons: + if "pokemon_data" in pokemon: + pokemon["pokemon_id"] = pokemon["pokemon_data"]["pokemon_id"] + pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + + if "name" not in pokemon and "pokemon_id" not in pokemon: + self.logger.warning("Strange result? %s" % pokemon) + # Skip this one! + continue + pokemon["distance"] = self.get_distance(self.bot.position, pokemon) - pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + + if "name" not in pokemon: + pokemon["name"] = inventory.pokemons().name_for(pokemon["pokemon_id"]) + if "pokemon_id" not in pokemon: + pokemon["pokemon_id"] = inventory.pokemons().id_for(pokemon["name"]) + pokemon["candies"] = inventory.candies().get(pokemon["pokemon_id"]).quantity # Pokemon also has a fort_id of the PokeStop the Pokemon is hiding at. # We should set our first destination at that Pokestop. @@ -364,8 +646,8 @@ def _is_vip_pokemon(self, pokemon): if self.bot.config.vips.get(pokemon["name"]) == {} or (self.config_treat_unseen_as_vip and not inventory.pokedex().seen(pokemon["pokemon_id"])): return True # If we must treat the family of the Pokemon as a VIP, also return true! - if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): - return True + # if self.config_treat_family_of_vip_as_vip and self._is_family_of_vip(pokemon): + # return True def _is_family_of_vip(self, pokemon): for fid in self.get_family_ids(pokemon): @@ -404,6 +686,8 @@ def get_worth_pokemons(self, pokemons, closest_first=False): else: worth_pokemons = [] + worth_pokemons += [p for p in pokemons if not inventory.pokedex().seen(p["pokemon_id"])] + if self.config_hunt_vip: worth_pokemons += [p for p in pokemons if p["name"] in self.bot.config.vips] @@ -483,3 +767,28 @@ def destination_vanished(self): self.logger.info("We lost {} {}(s) since {}".format(amount, self.destination["name"], self.hunt_started_at.strftime("%Y-%m-%d %H:%M:%S"))) return vanished + + def get_nearest_fort_on_the_way(self): + forts = self.bot.get_forts(order_by_distance=True) + + # Remove stops that are still on timeout + forts = filter(lambda x: x["id"] not in self.bot.fort_timeouts, forts) + i = 0 + while i < len(forts): + ratio = float(self.config.get('max_extra_dist_fort', 20)) + dist_self_to_fort = distance(self.bot.position[0], self.bot.position[1], forts[i]['latitude'], + forts[i]['longitude']) + # self.search_points[0][0], self.search_points[0][1] + dist_fort_to_pokemon = distance(self.search_points[0][0], self.search_points[0][1], forts[i]['latitude'], + forts[i]['longitude']) + total_dist = dist_self_to_fort + dist_fort_to_pokemon + dist_self_to_pokemon = distance(self.bot.position[0], self.bot.position[1], self.search_points[0][0], self.search_points[0][1]) + if total_dist < (1 + (ratio / 100)) * dist_self_to_pokemon: + i += 1 + else: + del forts[i] + # Return nearest fort if there are remaining + if len(forts): + return forts[0] + else: + return None diff --git a/pokemongo_bot/event_manager.py b/pokemongo_bot/event_manager.py index ae929dd48f..2be9f02f66 100644 --- a/pokemongo_bot/event_manager.py +++ b/pokemongo_bot/event_manager.py @@ -56,7 +56,7 @@ def emit(self, event, sender=None, level='info', formatted='', data={}): raise EventNotRegisteredException("Event %s not registered..." % event) if self._limit_output: - if (event == self._last_event) and (event in ["moving_to_fort", "moving_to_lured_fort", "position_update"]): + if (event == self._last_event) and (event in ["moving_to_fort", "moving_to_lured_fort", "position_update", "moving_to_hunter_target"]): stdout.write("\033[1A\033[0K\r") stdout.flush() diff --git a/pokemongo_bot/walkers/step_walker.py b/pokemongo_bot/walkers/step_walker.py index b558507011..8181a2fcfd 100644 --- a/pokemongo_bot/walkers/step_walker.py +++ b/pokemongo_bot/walkers/step_walker.py @@ -34,6 +34,9 @@ def step(self, speed=None): if speed is None: speed = uniform(self.bot.config.walk_min, self.bot.config.walk_max) + elif speed == self.bot.config.walk_max: + # Keep it more Human like... + speed = uniform(speed - 0.5, speed + 0.5) origin_lat, origin_lng, origin_alt = self.bot.position From 68d1a70f2c9a885ff2c68fd5a0100edbeca80776 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 13:08:22 +0200 Subject: [PATCH 04/10] Updated docs and added catch changes --- docs/configuration_files.md | 7 ++ pokemongo_bot/cell_workers/catch_pokemon.py | 76 +++++++++++++++------ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 48d798bcfd..97231e2a84 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -172,6 +172,8 @@ The behaviors of the bot are configured via the `tasks` key in the `config.json` [[back to top](#table-of-contents)] * CatchPokemon * `enabled`: Default "true" | Enable/Disable the task. + * `always_catch_family_of_vip`: Default "false" | Always catch family members of a VIP, even if locked on a target. + * `always_catch_trash`: Default "false" | Always catch trash Pokemon (12 candy evolve), even if locked on a target. * `treat_unseen_as_vip`: Default `"true"` | If true, treat new to dex as VIP * `catch_visible_pokemon`: Default "true" | If enabled, attempts to catch "visible" pokemon that are reachable * `catch_lured_pokemon`: Default "true" | If enabled, attempts to catch "lured" pokemon that are reachable @@ -1421,6 +1423,11 @@ Hunts down nearby Pokemon. Searches for Pokemon to complete the Pokedex, or if a * `treat_family_of_vip_as_vip`: `Default: false`. Should we see family of an VIP as a VIP (locking onto it if enabled) * `hunt_for_trash_to_fill_bag`: `Default: false`. Should we try to fill the bag with trash if a set amount of slots is left? * `trash_hunt_open_slots`: `Default: 25`. The amount of slots for the previous setting +* `run_to_vip`: `Default: false`. Run to a VIP Pokemon? Running sets the speed of the walker to the walk_max value! + +### Hunting family members of VIPs +If enabled (`target_family_of_vip` = true), the hunter will also hunt down family members of a VIP. For example, if you marked Gyarados as a VIP Pokemon then the hunter will now also hunt down Magikarps. +When on the hunt for a family member of a VIP, and `treat_family_of_vip_as_vip` is false, the hunter will keep a look out for "real" VIPs. So when hunting for a Magikarp, if a Snorlax shows up in the sightings, the hunter will target the Snorlax. ### Hunting for trash If enabled the hunter will start hunting down Pidgeys, Weedles and Caterpies when a set amount of slots (defaults to 25) are left in the bag to fill. The idea is simple; we are about to start evolving Pokemon. So the priority of the hunter shiftes. BUT when a VIP Pokemon is nearby, the Hunter will always start chasing that VIP first. diff --git a/pokemongo_bot/cell_workers/catch_pokemon.py b/pokemongo_bot/cell_workers/catch_pokemon.py index 7f6f2d9cf2..4c49db8154 100644 --- a/pokemongo_bot/cell_workers/catch_pokemon.py +++ b/pokemongo_bot/cell_workers/catch_pokemon.py @@ -71,36 +71,54 @@ def work(self): if hasattr(self.bot, "skipped_pokemon"): # Skip pokemon the catcher told us to ignore - self.pokemon = [ p for p in self.pokemon if p not in self.bot.skipped_pokemon ] + self.pokemon = [p for p in self.pokemon if p not in self.bot.skipped_pokemon] num_pokemon = len(self.pokemon) + always_catch_family_of_vip = self.config.get('always_catch_family_of_vip', False) + always_catch_trash = self.config.get('always_catch_trash', False) + trash_pokemon = ["Caterpie", "Weedle", "Pidgey", "Pidgeotto", "Pidgeot", "Kakuna", "Beedrill", "Metapod", "Butterfree"] if num_pokemon > 0: # try catching mon_to_catch = self.pokemon.pop() - is_vip = hasattr(mon_to_catch, "pokemon_id") and self._is_vip_pokemon(mon_to_catch['pokemon_id']) + is_vip = self._is_vip_pokemon(mon_to_catch) # hasattr(mon_to_catch, "pokemon_id") and self._is_vip_pokemon(mon_to_catch['pokemon_id']) # Always catch VIP Pokemons! if hasattr(self.bot,"hunter_locked_target") and self.bot.hunter_locked_target != None: bounty = self.bot.hunter_locked_target mon_name = Pokemons.name_for(mon_to_catch['pokemon_id']) bounty_name = Pokemons.name_for(bounty['pokemon_id']) - if mon_name != bounty_name and is_vip == False: - # This is not the Pokémon you are looking for... - self.logger.info("[Hunter locked a {}] Ignoring a {}".format(bounty_name, mon_name)) - self.ignored_while_looking.append(mon_to_catch['pokemon_id']) - - if num_pokemon > 1: - return WorkerResult.RUNNING - else: - return WorkerResult.SUCCESS - else: - # We have found a vip or our target... - if bounty_name == mon_name: - self.bot.hunter_locked_target = None - self.logger.info("Found my target {}!".format(bounty_name)) + family_catch = False + trash_catch = False + + if always_catch_trash: + if mon_name in trash_pokemon: + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + trash_catch = True + + if always_catch_family_of_vip: + if mon_name != bounty_name: + if self._is_family_of_vip(mon_to_catch['pokemon_id']): + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + family_catch = True + + if not family_catch and not trash_catch: + if (mon_name != bounty_name and is_vip is False): + # This is not the Pokémon you are looking for... + self.logger.info("[Hunter locked a {}] Ignoring a {}".format(bounty_name, mon_name)) + self.ignored_while_looking.append(mon_to_catch['pokemon_id']) + + if num_pokemon > 1: + return WorkerResult.RUNNING + else: + return WorkerResult.SUCCESS else: - self.logger.info("While on the hunt for {}, I found a {}! I need that Pokemon! Will try to catch...".format(bounty_name, mon_name)) + # We have found a vip or our target... + if bounty_name == mon_name: + self.bot.hunter_locked_target = None + self.logger.info("Found my target {}!".format(bounty_name)) + else: + self.logger.info("While on the hunt for {}, I found a {}! I want that Pokemon! Will try to catch...".format(bounty_name, mon_name)) try: if self.catch_pokemon(mon_to_catch) == WorkerResult.ERROR: # give up incase something went wrong in our catch worker (ran out of balls, etc) @@ -114,15 +132,23 @@ def work(self): # all pokemon have been processed return WorkerResult.SUCCESS - def _is_vip_pokemon(self, pokemon_id): + def _is_vip_pokemon(self, pokemon): + if 'pokemon_id' not in pokemon: + if not 'name' in pokemon: + return False + pokemon['pokemon_id'] = Pokemons.id_for(pokemon['name']) # having just a name present in the list makes them vip # Not seen pokemons also will become vip if it's not disabled in config - if self.bot.config.vips.get(Pokemons.name_for(pokemon_id)) == {}: + if self.bot.config.vips.get(Pokemons.name_for(pokemon['pokemon_id'])) == {}: return True - if (not inventory.pokedex().seen(pokemon_id)): + if (not inventory.pokedex().seen(pokemon['pokemon_id'])): return True + # If we must treat family of VIP as VIP + if self.config.get('treat_family_of_vip_as_vip', False): + if self._is_family_of_vip(pokemon['pokemon_id']): + return True # If we need the Pokemon for an evolution, catch it. - if any(not inventory.pokedex().seen(fid) for fid in self.get_family_ids(pokemon_id)): + if any(not inventory.pokedex().seen(fid) for fid in self.get_family_ids(pokemon['pokemon_id'])): # self.logger.info('Found a Pokemon whoes family is not yet complete in Pokedex!') return True @@ -267,6 +293,14 @@ def _have_applied_incense(self): return False return False + def _is_family_of_vip(self, pokemon_id): + for fid in self.get_family_ids(pokemon_id): + name = inventory.pokemons().name_for(fid) + if self.bot.config.vips.get(name) == {}: + return True + # No, not a family member of the VIP + return False + def get_family_ids(self, pokemon_id): family_id = inventory.pokemons().data_for(pokemon_id).first_evolution_id ids = [family_id] From 8a028e464e1b9d95c00286a768ddbaedcc3d79de Mon Sep 17 00:00:00 2001 From: David Westerink Date: Sun, 9 Jul 2017 15:07:23 +0200 Subject: [PATCH 05/10] Transfer fix! --- pokemongo_bot/cell_workers/bad_pokemon.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pokemongo_bot/cell_workers/bad_pokemon.py b/pokemongo_bot/cell_workers/bad_pokemon.py index e915d3e19f..6c0ae47694 100644 --- a/pokemongo_bot/cell_workers/bad_pokemon.py +++ b/pokemongo_bot/cell_workers/bad_pokemon.py @@ -18,6 +18,7 @@ def initialize(self): self.config_action_wait_min = self.config.get("action_wait_min", 3) self.config_action_wait_max = self.config.get("action_wait_max", 5) self.min_interval = self.config.get('min_interval', 120) + self.config_max_bulktransfer = self.config.get('max_bulktransfer', 100) self.next_update = None def work(self): From a2b6b7466cf1fcd55d5d3a3635142d3ba757c922 Mon Sep 17 00:00:00 2001 From: MerlionRock Date: Tue, 11 Jul 2017 17:21:13 +0800 Subject: [PATCH 06/10] check_niantic_api logic change Now check api uses version number returned from PGoAPI rather than comparing to Bossland endpoint. --- pokemongo_bot/__init__.py | 41 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 4c6e67bdce..295c139232 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -1068,20 +1068,14 @@ def login(self): formatted="Niantic Official API Version: {}".format(officalAPI) ) - link = "https://pokehash.buddyauth.com/api/hash/versions" - f = urllib2.urlopen(link) - myfile = f.read() - f.close() - bossland_hash_endpoint = myfile.split(",") - total_entry = int(len(bossland_hash_endpoint)) - last_bossland_entry = bossland_hash_endpoint[total_entry-1] - bossland_lastestAPI = last_bossland_entry.split(":")[0].replace('\"','') - hashingAPI_temp = 0 + PGoAPI_version = PGoApi.get_api_version() + PGoAPI_version_str = str(PGoAPI_version) + PGoAPI_version_str = "0."+ PGoAPI_version_str[0:2] + "." + PGoAPI_version_str[-1] self.event_manager.emit( 'security_check', sender=self, level='info', - formatted="Latest Bossland Hashing API Version: {}".format(bossland_lastestAPI) + formatted="Bot is currently running on API {}".format(PGoAPI_version_str) ) if self.config.check_niantic_api is True: @@ -1093,28 +1087,13 @@ def login(self): formatted="Warning: Bot is running on legacy API" ) else: - PGoAPI_hash_endpoint = HashServer.endpoint.split("com/",1)[1] - PGoAPI_hash_version = [] - # Check if PGoAPI hashing is in Bossland versioning - bossland_hash_data = json.loads(myfile) - - for version, endpoint in bossland_hash_data.items(): - if endpoint == PGoAPI_hash_endpoint: - # Version should always be in this format x.xx.x - # Check total len, if less than 4, pack a zero behind - if len(version.replace('.','')) < 4: - version = version + ".0" - hashingAPI_temp = int(version.replace('.','')) - # iOS versioning is always more than 1.19.0 - if hashingAPI_temp < 1190: - PGoAPI_hash_version.append(version) - # assuming andorid versioning is always last entry - PGoAPI_hash_version.sort(reverse=True) - # covert official api version & hashing api version to numbers officialAPI_int = int(officalAPI.replace('.','')) - hashingAPI_int = int(PGoAPI_hash_version[0].replace('.','')) + + PGoAPI_version_tmp = str(PGoAPI_version) + PGoAPI_version_tmp = PGoAPI_version_tmp[0:2] + PGoAPI_version_tmp[-1] + PGoAPI_version_int = int(PGoAPI_version_tmp) - if hashingAPI_int < officialAPI_int: + if PGoAPI_version_int < officialAPI_int: self.event_manager.emit( 'security_check', sender=self, @@ -1127,7 +1106,7 @@ def login(self): 'security_check', sender=self, level='info', - formatted="Current PGoAPI is using API Version: {}. Niantic API Check Pass".format(PGoAPI_hash_version[0]) + formatted="Current PGoAPI is using {} API. Niantic API Check Pass".format(PGoAPI_version_str) ) self.heartbeat() From 429b49ad3462c8da9bf8dcabf2f11a69aa516847 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 11 Jul 2017 13:11:44 +0200 Subject: [PATCH 07/10] Fix for bad_pokemon --- pokemongo_bot/inventory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index 0df46966df..52bb00025e 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -1001,6 +1001,7 @@ def __init__(self, data): self.in_fort = 'deployed_fort_id' in data self.is_favorite = data.get('favorite', 0) is 1 self.buddy_candy = data.get('buddy_candy_awarded', 0) + self.is_bad = data.get('is_bad', False) self.buddy_distance_needed = self.static.buddy_distance_needed self.fast_attack = FastAttacks.data_for(data['move_1']) From 6d85fa76d508c247a91728f3baec49c41a7715b9 Mon Sep 17 00:00:00 2001 From: MerlionRock Date: Wed, 12 Jul 2017 11:12:02 +0800 Subject: [PATCH 08/10] Updated Character Info Updated character information at login. Removed items that we no idea when they will ever appear in game, update some name to be the same as in game, added new items. --- pokemongo_bot/__init__.pyc | Bin 0 -> 48054 bytes pokemongo_bot/item_list.py | 15 +++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 pokemongo_bot/__init__.pyc diff --git a/pokemongo_bot/__init__.pyc b/pokemongo_bot/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd8e7e4df74d607d4aac13765cd26b01c684d55b GIT binary patch literal 48054 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 Date: Wed, 12 Jul 2017 11:13:43 +0800 Subject: [PATCH 09/10] Delete __init__.pyc --- pokemongo_bot/__init__.pyc | Bin 48054 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 pokemongo_bot/__init__.pyc diff --git a/pokemongo_bot/__init__.pyc b/pokemongo_bot/__init__.pyc deleted file mode 100644 index fd8e7e4df74d607d4aac13765cd26b01c684d55b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48054 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 Date: Wed, 12 Jul 2017 11:14:07 +0800 Subject: [PATCH 10/10] Updated character information Updated character information at login. Removed items that we no idea when they will ever appear in game, update some name to be the same as in game, added new items. --- pokemongo_bot/__init__.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 295c139232..08b7b208ca 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -1248,34 +1248,35 @@ def _print_character_info(self): # Items Output self.logger.info( 'PokeBalls: ' + str(items_inventory.get(1).count) + - ' | GreatBalls: ' + str(items_inventory.get(2).count) + - ' | UltraBalls: ' + str(items_inventory.get(3).count) + - ' | MasterBalls: ' + str(items_inventory.get(4).count)) + ' | Great Balls: ' + str(items_inventory.get(2).count) + + ' | Ultra Balls: ' + str(items_inventory.get(3).count) + + ' | Master Balls: ' + str(items_inventory.get(4).count)) self.logger.info( 'RazzBerries: ' + str(items_inventory.get(701).count) + ' | Nanab Berries: ' + str(items_inventory.get(703).count) + - ' | Pinap Berries: ' + str(items_inventory.get(705).count)) + ' | Pinap Berries: ' + str(items_inventory.get(705).count) + + ' | Golden RazzBerries: ' + str(items_inventory.get(706).count) + + ' | Golden Nanab Berries: ' + str(items_inventory.get(707).count) + + ' | Golden Pinap Berries: ' + str(items_inventory.get(708).count)) self.logger.info( 'LuckyEgg: ' + str(items_inventory.get(301).count) + - ' | Incubator: ' + str(items_inventory.get(902).count) + - ' | TroyDisk: ' + str(items_inventory.get(501).count)) + ' | Incubator: ' + str(items_inventory.get(902).count)) self.logger.info( 'Potion: ' + str(items_inventory.get(101).count) + - ' | SuperPotion: ' + str(items_inventory.get(102).count) + - ' | HyperPotion: ' + str(items_inventory.get(103).count) + - ' | MaxPotion: ' + str(items_inventory.get(104).count)) + ' | Super Potion: ' + str(items_inventory.get(102).count) + + ' | Hyper Potion: ' + str(items_inventory.get(103).count) + + ' | Max Potion: ' + str(items_inventory.get(104).count)) self.logger.info( 'Incense: ' + str(items_inventory.get(401).count) + - ' | IncenseSpicy: ' + str(items_inventory.get(402).count) + - ' | IncenseCool: ' + str(items_inventory.get(403).count)) + ' | Lure Module: ' + str(items_inventory.get(501).count)) self.logger.info( 'Revive: ' + str(items_inventory.get(201).count) + - ' | MaxRevive: ' + str(items_inventory.get(202).count)) + ' | Max Revive: ' + str(items_inventory.get(202).count)) self.logger.info( 'Sun Stone: ' + str(items_inventory.get(1101).count) + @@ -1283,6 +1284,14 @@ def _print_character_info(self): ' | Metal Coat: ' + str(items_inventory.get(1103).count) + ' | Dragon Scale: ' + str(items_inventory.get(1104).count) + ' | Upgrade: ' + str(items_inventory.get(1105).count)) + + self.logger.info( + 'Fast TM: ' + str(items_inventory.get(1201).count) + + ' | Charge TM: ' + str(items_inventory.get(1202).count) + + ' | Rare Candy: ' + str(items_inventory.get(1301).count) + + ' | Free Raid Pass: ' + str(items_inventory.get(1401).count) + + ' | Premium Raid Pass: ' + str(items_inventory.get(1402).count) + + ' | Legendary Raid Pass: ' + str(items_inventory.get(1403).count)) if warn: self.logger.info('')