From 91cd43564d5d90288e35b8fbd32ce7dfb862f326 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 11 Jul 2017 12:54:14 +0200 Subject: [PATCH 01/38] Adjust modifiers for moves --- pokemongo_bot/inventory.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index 0df46966df..6cb08cbdcb 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -1393,11 +1393,12 @@ def retrieve_inventories_size(self): # STAB (Same-type attack bonus) # Factor applied to attack of the same type as pokemon -STAB_FACTOR = 1.25 +STAB_FACTOR = 1.2 # Factor applied to attack when it's effective against defending pokemon type -EFFECTIVENESS_FACTOR = 1.25 +EFFECTIVENESS_FACTOR = 1.4 # Factor applied to attack when it's weak against defending pokemon type -RESISTANCE_FACTOR = 0.8 +RESISTANCE_FACTOR = 0.714 +IMMUNITY_FACTOR = 0.51 _inventory = None # type: Inventory From 95a3ef93cdcac3734eaaa7efe0d94cae232ab9d1 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 11 Jul 2017 20:40:10 +0200 Subject: [PATCH 02/38] Also need to adjust the tests... --- tests/inventory_test.py | 2 +- tests/nickname_test.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/inventory_test.py b/tests/inventory_test.py index 0a236daf42..a556eda5c3 100644 --- a/tests/inventory_test.py +++ b/tests/inventory_test.py @@ -133,7 +133,7 @@ def test_pokemons(self): self.assertEqual(poke.nickname, "Golb") self.assertEqual(poke.nickname_raw, poke.nickname) self.assertAlmostEqual(poke.moveset.dps,15.130190007037298 ) - self.assertAlmostEqual(poke.moveset.dps_attack, 16.5376495425756) + self.assertAlmostEqual(poke.moveset.dps_attack, 16.256157635467982) self.assertAlmostEqual(poke.moveset.dps_defense, 6.377929397804805 ) self.assertAlmostEqual(poke.moveset.attack_perfection, 0.1976822769744798) self.assertAlmostEqual(poke.moveset.defense_perfection, 0.62438387986335) diff --git a/tests/nickname_test.py b/tests/nickname_test.py index a547594e3a..a3859cd831 100644 --- a/tests/nickname_test.py +++ b/tests/nickname_test.py @@ -37,7 +37,7 @@ def test_nickname_generation(self): self.assertNicks('{fast_attack_char}', ['l', 'n']) self.assertNicks('{charged_attack_char}', ['h', 'N']) self.assertNicks('{attack_code}', ['lh', 'nN']) - self.assertNicks('{attack_pct}', ['020', '078']) + self.assertNicks('{attack_pct}', ['015', '076']) self.assertNicks('{attack_pct2}', ['20', '77']) self.assertNicks('{attack_pct1}', ['2', '7']) self.assertNicks('{defense_pct}', ['062', '008']) From d1b061abac85b9d67e1a8224a21eddd4c588596c Mon Sep 17 00:00:00 2001 From: David Westerink Date: Thu, 13 Jul 2017 01:04:33 +0200 Subject: [PATCH 03/38] Fixed all the tests --- tests/inventory_test.py | 6 +++--- tests/nickname_test.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/inventory_test.py b/tests/inventory_test.py index a556eda5c3..7b18342984 100644 --- a/tests/inventory_test.py +++ b/tests/inventory_test.py @@ -135,7 +135,7 @@ def test_pokemons(self): self.assertAlmostEqual(poke.moveset.dps,15.130190007037298 ) self.assertAlmostEqual(poke.moveset.dps_attack, 16.256157635467982) self.assertAlmostEqual(poke.moveset.dps_defense, 6.377929397804805 ) - self.assertAlmostEqual(poke.moveset.attack_perfection, 0.1976822769744798) + self.assertAlmostEqual(poke.moveset.attack_perfection, 0.15433958252117524) self.assertAlmostEqual(poke.moveset.defense_perfection, 0.62438387986335) poke = Pokemon({ @@ -153,9 +153,9 @@ def test_pokemons(self): self.assertEqual(poke.nickname, poke.name) self.assertEqual(poke.nickname_raw, '') self.assertAlmostEqual(poke.moveset.dps, 17.333333333333332 ) - self.assertAlmostEqual(poke.moveset.dps_attack, 21.666666666666668) + self.assertAlmostEqual(poke.moveset.dps_attack, 20.8) self.assertAlmostEqual(poke.moveset.dps_defense, 4.814814814814815) - self.assertAlmostEqual(poke.moveset.attack_perfection, 0.777011494252873) + self.assertAlmostEqual(poke.moveset.attack_perfection, 0.7607623318385653) self.assertAlmostEqual(poke.moveset.defense_perfection, 0.08099928856783224) def test_levels_to_cpm(self): diff --git a/tests/nickname_test.py b/tests/nickname_test.py index a3859cd831..34d0cb733e 100644 --- a/tests/nickname_test.py +++ b/tests/nickname_test.py @@ -38,8 +38,8 @@ def test_nickname_generation(self): self.assertNicks('{charged_attack_char}', ['h', 'N']) self.assertNicks('{attack_code}', ['lh', 'nN']) self.assertNicks('{attack_pct}', ['015', '076']) - self.assertNicks('{attack_pct2}', ['20', '77']) - self.assertNicks('{attack_pct1}', ['2', '7']) + self.assertNicks('{attack_pct2}', ['15', '75']) + self.assertNicks('{attack_pct1}', ['1', '7']) self.assertNicks('{defense_pct}', ['062', '008']) self.assertNicks('{defense_pct2}', ['62', '08']) self.assertNicks('{defense_pct1}', ['6', '1']) @@ -62,7 +62,7 @@ def test_nickname_generation(self): ['48_46_948', '38_44_6E0']) self.assertNicks( '{attack_code}{attack_pct1}{defense_pct1}{ivcp_pct1}{name}', - ['lh264Golbat', 'nN713Rattata']) + ['lh164Golbat', 'nN713Rattata']) # def setUp(self): From 629eb6080d61767d72a771669a888dd6d93ad940 Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 18:08:20 +0200 Subject: [PATCH 04/38] Add files via upload --- requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index d517615426..b6b5efa4f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.11.0 networkx==1.11 six==1.10 -git+https://github.com/pogodevorg/pgoapi.git@develop#egg=pgoapi +git+https://github.com/goedzo/pgoapi.git@develop#egg=pgoapi geopy==1.11.0 geographiclib==1.46.3 requests==2.10.0 @@ -18,7 +18,7 @@ websocket-client==0.37.0 python-socketio==1.4.2 flask==0.11.1 socketIO_client==0.7.0 -eventlet==0.19.0 +eventlet==0.20.1 gpxpy==1.1.1 mock==2.0.0 timeout-decorator==0.3.2 @@ -31,4 +31,4 @@ Geohash==1.0 python-telegram-bot==5.3.0 discord_simple==0.0.1.15 urllib3==1.18 -selenium==2.53.6 \ No newline at end of file +selenium==2.53.6 From a24839e14705c0f8aa0012dbf6d260463d993565 Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 18:18:15 +0200 Subject: [PATCH 05/38] Add Full and complete 0.67.2 API Support Update the API to use the full PROTOS implementation from: https://github.com/AeonLucid/POGOProtos --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b6b5efa4f0..691f050f1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ websocket-client==0.37.0 python-socketio==1.4.2 flask==0.11.1 socketIO_client==0.7.0 -eventlet==0.20.1 +eventlet==0.19.0 gpxpy==1.1.1 mock==2.0.0 timeout-decorator==0.3.2 From 4b4f65aeeaac528a9cbe01dc899ba2194917d215 Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 18:23:09 +0200 Subject: [PATCH 06/38] Changes docker to Goedzo Fork --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b5bdc16128..63b55d7e53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ FROM alpine -ARG BUILD_REPO=PokemonGoF/PokemonGo-Bot +ARG BUILD_REPO=goedzo/PokemonGo-Bot ARG BUILD_BRANCH=master LABEL build_repo=$BUILD_REPO build_branch=$BUILD_BRANCH From 9db3b8d7be74f28903e3a0cf1aec970ceba51bba Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 19:21:51 +0200 Subject: [PATCH 07/38] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 691f050f1e..9fb17d80a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ websocket-client==0.37.0 python-socketio==1.4.2 flask==0.11.1 socketIO_client==0.7.0 -eventlet==0.19.0 +eventlet==0.20.0 gpxpy==1.1.1 mock==2.0.0 timeout-decorator==0.3.2 From 22ff6eba9f5e733fdb278917e98fcc438a202715 Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 19:24:27 +0200 Subject: [PATCH 08/38] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9fb17d80a8..691f050f1e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ websocket-client==0.37.0 python-socketio==1.4.2 flask==0.11.1 socketIO_client==0.7.0 -eventlet==0.20.0 +eventlet==0.19.0 gpxpy==1.1.1 mock==2.0.0 timeout-decorator==0.3.2 From 8b9e3b78920884315e72f49097392c00386c6710 Mon Sep 17 00:00:00 2001 From: goedzo Date: Thu, 13 Jul 2017 19:36:09 +0200 Subject: [PATCH 09/38] Making sure that Original Pogo Branch is used --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 63b55d7e53..b5bdc16128 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ FROM alpine -ARG BUILD_REPO=goedzo/PokemonGo-Bot +ARG BUILD_REPO=PokemonGoF/PokemonGo-Bot ARG BUILD_BRANCH=master LABEL build_repo=$BUILD_REPO build_branch=$BUILD_BRANCH From a8116508523a295f8b1308d7d4cc7be81afac0f5 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 14:07:16 +0200 Subject: [PATCH 10/38] Removing /r Removing the Extra /r since it not part of the source file. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 691f050f1e..f76d0fc5f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,4 +31,4 @@ Geohash==1.0 python-telegram-bot==5.3.0 discord_simple==0.0.1.15 urllib3==1.18 -selenium==2.53.6 +selenium==2.53.6 \ No newline at end of file From b5acdd5f3394f7de40926cacfd2ca35ae019cad4 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 14:29:04 +0200 Subject: [PATCH 11/38] Adding Gym Cell Worker to deal with Gym Messages --- pokemongo_bot/cell_workers/gym_pokemon.py | 673 ++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 pokemongo_bot/cell_workers/gym_pokemon.py diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py new file mode 100644 index 0000000000..638de267de --- /dev/null +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -0,0 +1,673 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from __future__ import absolute_import + +from datetime import datetime, timedelta +import sys +import time +import random +from random import uniform +from collections import Counter + +from pgoapi.utilities import f2i +from pokemongo_bot import inventory +from pokemongo_bot.inventory import player + +from pokemongo_bot.constants import Constants +from pokemongo_bot.human_behaviour import action_delay, sleep +from pokemongo_bot.worker_result import WorkerResult +from pokemongo_bot.base_task import BaseTask +from pokemongo_bot import inventory +from .utils import distance, format_time, fort_details, format_dist +from pokemongo_bot.tree_config_builder import ConfigException +from pokemongo_bot.walkers.walker_factory import walker_factory +from pokemongo_bot.inventory import Pokemons + +GYM_DETAIL_RESULT_SUCCESS = 1 +GYM_DETAIL_RESULT_OUT_OF_RANGE = 2 +GYM_DETAIL_RESULT_UNSET = 0 + +TEAM_BLUE = 1 +TEAM_RED = 2 +TEAM_YELLOW = 3 + +TEAMS = { + 1: "Mystic", + 2: "Valor", + 3: "Instinct" +} + +ITEM_RAZZBERRY = 701 +ITEM_NANABBERRY = 703 +ITEM_PINAPBERRY = 705 + + +class GymPokemon(BaseTask): + SUPPORTED_TASK_API_VERSION = 1 + + def __init__(self, bot, config): + super(GymPokemon, self).__init__(bot, config) + + def initialize(self): + # 10 seconds from current time + self.next_update = datetime.now() + timedelta(0, 10) + self.order_by = self.config.get('order_by', 'cp') + self.min_interval = self.config.get('min_interval', 60) + self.min_recheck = self.config.get('min_recheck', 30) + self.max_recheck = self.config.get('max_recheck', 120) + self.recheck = datetime.now() + self.walker = self.config.get('walker', 'StepWalker') + self.destination = None + self.recent_gyms = [] + self.pokemons = [] + self.fort_pokemons = [] + self.expire_recent = 10 + self.next_expire = None + self.dropped_gyms = [] + self.blacklist= [] + self.check_interval = 0 + self.gyms = [] + self.raid_gyms = dict() + self.team = self.bot.player_data['team'] + + def should_run(self): + # Check if we have any Pokemons and are level > 5 + return player()._level >= 5 and len(self.pokemons) > 0 + + def display_fort_pokemon(self): + if len(self.fort_pokemons) == 0: + return + self.logger.info("We currently have %s Pokemon in Gym(s)" % len(self.fort_pokemons) ) + for pokemon in self.fort_pokemons: + lat = self.bot.position[0:2][0] + lng = self.bot.position[0:2][1] + details = fort_details(self.bot, pokemon.fort_id, lat, lng) + fort_name = details.get('name', 'Unknown') + self.logger.info("%s: %s (%s CP)" % (fort_name, pokemon.name, pokemon.cp)) + + def work(self): + self.pokemons = inventory.pokemons().all() + self.fort_pokemons = [p for p in self.pokemons if p.in_fort] + self.pokemons = [p for p in self.pokemons if not p.in_fort] + + self.dropped_gyms = [] + for pokemon in self.fort_pokemons: + self.dropped_gyms.append(pokemon.fort_id) + + if self._should_print(): + self.display_fort_pokemon() + self._compute_next_update() + # Do display teh stats about Pokemon in Gym and collection time [please] + if not self.enabled: + return WorkerResult.SUCCESS + if self.bot.softban: + return WorkerResult.SUCCESS + + if len(self.fort_pokemons) >= 20: + if self._should_print(): + self.logger.info("We have a max of 20 Pokemon in gyms.") + return WorkerResult.SUCCESS + + if hasattr(self.bot, "hunter_locked_target") and self.bot.hunter_locked_target is not None: + # Don't move to a gym when hunting for a Pokemon + return WorkerResult.SUCCESS + + if self.destination is None: + self.check_close_gym() + + if not self.should_run(): + return WorkerResult.SUCCESS + + if self.destination is None: + self.determin_new_destination() + + if self.destination is not None: + result = self.move_to_destination() + # Can return RUNNING to move to a gym + return result + + + return WorkerResult.SUCCESS + + def check_close_gym(self): + # Check if we are walking past a gym + close_gyms = self.get_gyms_in_range() + # Filter active raids from the gyms + close_gyms = filter(lambda gym: gym["id"] not in self.raid_gyms, close_gyms) + + if len(close_gyms) > 0: + # self.logger.info("Walking past a gym!") + for gym in close_gyms: + if gym["id"] in self.dropped_gyms: + continue + + gym_details = self.get_gym_details(gym) + if gym_details: + pokes = self._get_pokemons_in_gym(gym_details) + if len(pokes) == 6: + continue + if 'enabled' in gym: + if not gym['enabled']: + continue + if 'owned_by_team' in gym: + if gym["owned_by_team"] == self.team: + # self.feed_pokemons_in_gym(gym) + + if 'gym_display' in gym: + display = gym['gym_display'] + if 'slots_available' in display: + self.logger.info("Gym has %s open spots!" % display['slots_available']) + if display['slots_available'] > 0 and gym["id"] not in self.dropped_gyms: + self.logger.info("Dropping pokemon in %s" % gym_details["name"]) + self.drop_pokemon_in_gym(gym, pokes) + if self.destination is not None and gym["id"] == self.destination["id"]: + self.destination = None + return WorkerResult.SUCCESS + else: + self.logger.info("Neutral gym? %s" % gym) + self.logger.info("Dropping pokemon in %s" % gym_details["name"]) + self.drop_pokemon_in_gym(gym, []) + if self.destination is not None and gym["id"] == self.destination["id"]: + self.destination = None + return WorkerResult.SUCCESS + + def determin_new_destination(self): + gyms = self.get_gyms() + if len(gyms) == 0: + if len(self.recent_gyms) == 0 and self._should_print(): + self.logger.info("No Gyms in range to scan!") + return WorkerResult.SUCCESS + + self.logger.info("Inspecting %s gyms." % len(gyms)) + self.logger.info("Recent gyms: %s" % len(self.recent_gyms)) + self.logger.info("Active raid gyms: %s" % len(self.raid_gyms)) + teams = [] + for gym in gyms: + # Ignore after done for 5 mins + self.recent_gyms.append(gym["id"]) + + if 'enabled' in gym: + # Gym can be closed for a raid or something, skipp to the next + if not gym['enabled']: + continue + + if 'owned_by_team' in gym: + if gym["owned_by_team"] == 1: + teams.append("Mystic") + elif gym["owned_by_team"] == 2: + teams.append("Valor") + elif gym["owned_by_team"] == 3: + teams.append("Instinct") + # else: + # self.logger.info("Unknown team? %s" % gym) + + if gym["owned_by_team"] == self.team: + if 'gym_display' in gym: + display = gym['gym_display'] + if 'slots_available' in display: + self.logger.info("Gym has %s open spots!" % display['slots_available']) + self.destination = gym + break + else: + # self.logger.info("Found a Neutral gym?") + # self.logger.info("Info: %s" % gym) + self.destination = gym + break + if len(teams) > 0: + count_teams = Counter(teams) + self.logger.info("Gym Teams %s", ", ".join('{}({})'.format(key, val) for key, val in count_teams.items())) + + def move_to_destination(self): + if self.check_interval >= 4: + self.check_interval = 0 + gyms = self.get_gyms() + for g in gyms: + if g["id"] == self.destination["id"]: + # self.logger.info("Inspecting target: %s" % g) + if "owned_by_team" in g and g["owned_by_team"] is not self.team: + self.logger.info("Damn! Team %s took gym before we arrived!" % TEAMS[g["owned_by_team"]]) + self.destination = None + return WorkerResult.SUCCESS + break + else: + self.check_interval += 1 + + # Moving to a gym to deploy Pokemon + unit = self.bot.config.distance_unit # Unit to use when printing formatted distance + lat = self.destination["latitude"] + lng = self.destination["longitude"] + details = fort_details(self.bot, self.destination["id"], lat, lng) + gym_name = details.get('name', 'Unknown') + + dist = distance( + self.bot.position[0], + self.bot.position[1], + lat, + lng + ) + noised_dist = distance( + self.bot.noised_position[0], + self.bot.noised_position[1], + lat, + lng + ) + + moving = noised_dist > Constants.MAX_DISTANCE_FORT_IS_REACHABLE if self.bot.config.replicate_gps_xy_noise else dist > Constants.MAX_DISTANCE_FORT_IS_REACHABLE + + if moving: + fort_event_data = { + 'fort_name': u"{}".format(gym_name), + 'distance': format_dist(dist, unit), + } + self.emit_event( + 'moving_to_fort', + formatted="Moving towards Gym {fort_name} - {distance}", + data=fort_event_data + ) + + step_walker = walker_factory(self.walker, self.bot, lat, lng) + + if not step_walker.step(): + return WorkerResult.RUNNING + else: + self.emit_event( + 'arrived_at_fort', + formatted=("Arrived at Gym %s." % gym_name) + ) + gym_details = self.get_gym_details(self.destination) + current_pokemons = self._get_pokemons_in_gym(gym_details) + self.drop_pokemon_in_gym(self.destination, current_pokemons) + # Feed the Pokemon now we're here... + # self.feed_pokemons_in_gym(self.destination) + self.destination = None + # Look around if there are more gyms to fill + self.determin_new_destination() + # If there is none, we're done, else we go to the next! + if self.destination is None: + return WorkerResult.SUCCESS + else: + return WorkerResult.RUNNING + + def get_gym_details(self, gym): + lat = gym['latitude'] + lng = gym['longitude'] + + in_reach = False + + if self.bot.config.replicate_gps_xy_noise: + if distance(self.bot.noised_position[0], self.bot.noised_position[1], gym['latitude'], gym['longitude']) <= Constants.MAX_DISTANCE_FORT_IS_REACHABLE: + in_reach = True + else: + if distance(self.bot.position[0], self.bot.position[1], gym['latitude'], gym['longitude']) <= Constants.MAX_DISTANCE_FORT_IS_REACHABLE: + in_reach = True + + if in_reach: + request = self.bot.api.create_request() + request.gym_get_info(gym_id=gym['id'], gym_lat_degrees=lat, gym_lng_degrees=lng, player_lat_degrees=self.bot.position[0],player_lng_degrees=self.bot.position[1]) + response_dict = request.call() + + if ('responses' in response_dict) and ('GYM_GET_INFO' in response_dict['responses']): + details = response_dict['responses']['GYM_GET_INFO'] + return details + else: + return False + # details = fort_details(self.bot, , lat, lng) + # fort_name = details.get('name', 'Unknown') + # self.logger.info("Checking Gym: %s (%s pts)" % (fort_name, gym['gym_points'])) + + def _get_pokemons_in_gym(self, gym_details): + pokemon_names = [] + gym_info = gym_details.get('gym_status_and_defenders', None) + if gym_info: + defenders = gym_info.get('gym_defender', []) + for defender in defenders: + motivated_pokemon = defender.get('motivated_pokemon') + pokemon_info = motivated_pokemon.get('pokemon') + pokemon_id = pokemon_info.get('pokemon_id') + pokemon_names.append(Pokemons.name_for(pokemon_id)) + + return pokemon_names + + def feed_pokemons_in_gym(self, gym): + berries = inventory.items().get(ITEM_RAZZBERRY).count + (inventory.items().get(ITEM_PINAPBERRY).count - 10) + inventory.items().get(ITEM_NANABBERRY).count + if berries < 1: + self.logger.info("No berries left to feed Pokemon.") + return True + + max_gym_time = timedelta(hours=8,minutes=20) + gym_info = self.get_gym_details(gym).get('gym_status_and_defenders', None) + # self.logger.info("Defenders in gym:")okemon_info..items()get('pokemon_id') + if gym_info: + defenders = gym_info.get('gym_defender', []) + for defender in defenders: + motivated_pokemon = defender.get('motivated_pokemon') + pokemon_info = motivated_pokemon.get('pokemon') + pokemon_id = pokemon_info.get('pokemon_id') + # timestamp when deployed + deployed_on = datetime.fromtimestamp(int(motivated_pokemon.get('deploy_ms')) / 1e3) + time_deployed = datetime.now() - deployed_on + # % of motivation + current_motivation = motivated_pokemon.get('motivation_now') + + # Let's see if we should feed this Pokemon + if time_deployed < max_gym_time and current_motivation < 1.0: + # Let's feed this Pokemon a candy + # self.logger.info("This pokemon deserves a candy") + berry_id = self._determin_feed_berry_id(motivated_pokemon) + poke_id = pokemon_info.get('id') + quantity = pokemon_info.get('num_upgrades') + self._feed_pokemon(gym, poke_id, berry_id, quantity) + + def _determin_feed_berry_id(self, motivated_pokemon): + # # Store the amount of berries we have + razzb = inventory.items().get(ITEM_RAZZBERRY).count + pinap = inventory.items().get(ITEM_PINAPBERRY).count + nanab = inventory.items().get(ITEM_NANABBERRY).count + missing_motivation = 1.0 - motivated_pokemon.get('motivation_now') + # Always allow feeding with RAZZ and NANAB + allowed_berries = [] + if razzb > 0: + allowed_berries.append(ITEM_RAZZBERRY) + + if nanab > 0: + allowed_berries.append(ITEM_NANABBERRY) + + if pinap > 10: + allowed_berries.append(ITEM_PINAPBERRY) + + food_values = motivated_pokemon['food_value'] + # Only check the berries we wish to feed + food_values = [f for f in food_values if f['food_item'] in allowed_berries] + # Sort by the least restore first + sorted(food_values, key=lambda x: x['motivation_increase']) + + for food_value in food_values: + if food_value['motivation_increase'] >= missing_motivation: + # We fully restore CP with this berry! + return food_value['food_item'] + # Okay, we can't completely fill the CP for the pokemon, get the best berry then NO GOLDEN + return food_values[-1]['food_item'] + + def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): + request = self.bot.api.create_request() + request.gym_feed_pokemon( + starting_quantity=quantity, + item_id=berry_id, + gym_id=gym["id"], + pokemon_id=pokemon_id, + player_lat_degrees=f2i(self.bot.position[0]), + player_lng_degrees=f2i(self.bot.position[1]) + ) + response_dict = request.call() + if ('responses' in response_dict) and ('GYM_FEED_POKEMON' in response_dict['responses']): + feeding = response_dict['responses']['GYM_FEED_POKEMON'] + result = feeding.get('result', -1) + self.logger.info("Feeding: %s" % feeding) + if result == 1: + # Succesful feeding + self.logger.info("Fed a Pokemon!") + else: + self.logger.info("Feeding failed! %s" % result) + + def drop_pokemon_in_gym(self, gym, current_pokemons): + self.pokemons = inventory.pokemons().all() + self.fort_pokemons = [p for p in self.pokemons if p.in_fort] + self.pokemons = [p for p in self.pokemons if not p.in_fort] + close_gyms = self.get_gyms_in_range() + + empty_gym = False + + for pokemon in self.fort_pokemons: + if pokemon.fort_id == gym["id"]: + self.logger.info("We are already in this gym!") + if pokemon.fort_id not in self.dropped_gyms: + self.dropped_gyms.append(pokemon.fort_id) + self.recent_gyms.append(gym["id"]) + return WorkerResult.SUCCESS + + for g in close_gyms: + if g["id"] == gym["id"]: + if 'owned_by_team' in g: + self.logger.info("Expecting team: %s it is: %s" % (self.bot.player_data['team'], g["owned_by_team"]) ) + if g["owned_by_team"] is not self.team: + self.logger.info("Can't drop in a enemy gym!") + self.recent_gyms.append(gym["id"]) + return WorkerResult.SUCCESS + else: + self.logger.info("Empty gym?? %s" % g) + gym_details = self.get_gym_details(gym) + self.logger.info("Details: %s" % gym_details) + empty_gym = True + if not gym_details or gym_details == {}: + self.logger.info("No details for this Gym? Blacklisting!") + self.blacklist.append(gym["id"]) + return WorkerResult.SUCCESS + + # Check for raid + if 'raid_info' in gym: + raid_info = gym["raid_info"] + raid_starts = datetime.fromtimestamp(int(raid_info["raid_battle_ms"]) / 1e3) + raid_ends = datetime.fromtimestamp(int(raid_info["raid_end_ms"]) / 1e3) + self.logger.info("Raid starts: %s" % raid_starts.strftime('%Y-%m-%d %H:%M:%S.%f')) + self.logger.info("Raid ends: %s" % raid_ends.strftime('%Y-%m-%d %H:%M:%S.%f')) + t = datetime.today() + + if raid_starts < datetime.now(): + self.logger.info("Active raid?") + if raid_ends < datetime.now(): + self.logger.info("No need to wait.") + elif (raid_ends-t).seconds > 600: + self.logger.info("Need to wait long than 10 minutes, skipping") + self.destination = None + self.recent_gyms.append(gym["id"]) + self.raid_gyms[gym["id"]] = raid_ends + return WorkerResult.SUCCESS + else: + while raid_ends > datetime.now(): + self.logger.info("Waiting for %s seconds for raid to end..." % (raid_ends-datetime.today()).seconds) + if (raid_ends-datetime.today()).seconds > 20: + sleep(20) + else: + sleep((raid_ends-datetime.today()).seconds) + break + else: + self.logger.info("Raid has not begun yet!") + + if 'same_team_deploy_lockout_end_ms' in gym: + # self.logger.info("%f" % gym["same_team_deploy_lockout_end_ms"]) + org_time = int(gym["same_team_deploy_lockout_end_ms"]) / 1e3 + lockout_time = datetime.fromtimestamp(org_time) + t = datetime.today() + + if lockout_time > datetime.now(): + self.logger.info("Lockout time: %s" % lockout_time.strftime('%Y-%m-%d %H:%M:%S.%f')) + while lockout_time > datetime.now(): + self.logger.info("Waiting for %s seconds deployment lockout to end..." % (lockout_time-datetime.today()).seconds) + if (lockout_time-datetime.today()).seconds > 20: + sleep(20) + else: + sleep((lockout_time-t).seconds) + break + + #FortDeployPokemon + # self.logger.info("Trying to deploy Pokemon in gym: %s" % gym) + gym_details = self.get_gym_details(gym) + # self.logger.info("Gym details: %s" % gym_details) + fort_pokemon = self._get_best_pokemon(current_pokemons) + pokemon_id = fort_pokemon.unique_id + # self.logger.info("Trying to deploy %s (%s)" % (fort_pokemon, pokemon_id)) + # self.logger.info("Gym in control by %s. I am on team %s" % (gym["owned_by_team"], self.bot.player_data['team'])) + + request = self.bot.api.create_request() + request.gym_deploy( + fort_id=gym["id"], + pokemon_id=pokemon_id, + player_lat_degrees=f2i(self.bot.position[0]), + player_lng_degrees=f2i(self.bot.position[1]) + ) + # self.logger.info("Req: %s" % request) + response_dict = request.call() + # self.logger.info("Called deploy pokemon: %s" % response_dict) + + if ('responses' in response_dict) and ('GYM_DEPLOY' in response_dict['responses']): + deploy = response_dict['responses']['GYM_DEPLOY'] + result = response_dict.get('status_code', -1) + self.recent_gyms.append(gym["id"]) + # self.logger.info("Status: %s" % result) + if result == 1: + self.dropped_gyms.append(gym["id"]) + gym_details = self.get_gym_details(gym) + # SUCCES + self.logger.info("We deployed %s (%s CP) in the gym! We now have %s Pokemon in gyms!" % (fort_pokemon.name, fort_pokemon.cp, len(self.dropped_gyms))) + self.emit_event( + 'deployed_pokemon', + formatted=("We deployed %s (%s CP) in the gym %s!!" % (fort_pokemon.name, fort_pokemon.cp, gym_details["name"])), + data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} + ) + return WorkerResult.SUCCESS + elif result == 2: + #ERROR_ALREADY_HAS_POKEMON_ON_FORT + self.logger.info('ERROR_ALREADY_HAS_POKEMON_ON_FORT') + self.dropped_gyms.append(gym["id"]) + return WorkerResult.ERROR + elif result == 3: + #ERROR_OPPOSING_TEAM_OWNS_FORT + self.logger.info('ERROR_OPPOSING_TEAM_OWNS_FORT') + return WorkerResult.ERROR + elif result == 4: + #ERROR_FORT_IS_FULL + self.logger.info('ERROR_FORT_IS_FULL') + return WorkerResult.ERROR + elif result == 5: + #ERROR_NOT_IN_RANGE + self.logger.info('ERROR_NOT_IN_RANGE') + return WorkerResult.ERROR + elif result == 6: + #ERROR_PLAYER_HAS_NO_TEAM + self.logger.info('ERROR_PLAYER_HAS_NO_TEAM') + return WorkerResult.ERROR + elif result == 7: + #ERROR_POKEMON_NOT_FULL_HP + self.logger.info('ERROR_POKEMON_NOT_FULL_HP') + return WorkerResult.ERROR + elif result == 8: + #ERROR_PLAYER_BELOW_MINIMUM_LEVEL + self.logger.info('ERROR_PLAYER_BELOW_MINIMUM_LEVEL') + return WorkerResult.ERROR + elif result == 8: + #ERROR_POKEMON_IS_BUDDY + self.logger.info('ERROR_POKEMON_IS_BUDDY') + return WorkerResult.ERROR + + def get_gyms(self, skip_recent_filter=False): + if len(self.gyms) == 0: + self.gyms = self.bot.get_gyms(order_by_distance=True) + + if self._should_recheck(): + self.gyms = self.bot.get_gyms(order_by_distance=True) + self._compute_next_recheck() + + if self._should_expire(): + self.recent_gyms = [] + self._compute_next_expire() + # Check raid gyms for raids that ended + for gym_id in list(self.raid_gyms.keys()): + if self.raid_gyms[gym_id] < datetime.now(): + self.logger.info("Raid at %s ended (%s)" % (gym_id, self.raid_gyms[gym_id])) + del(self.raid_gyms[gym_id]) + + gyms = [] + # if not skip_recent_filter: + gyms = filter(lambda gym: gym["id"] not in self.recent_gyms, self.gyms) + # Filter blacklisted gyms + gyms = filter(lambda gym: gym["id"] not in self.blacklist, gyms) + # Filter out gyms we already in + gyms = filter(lambda gym: gym["id"] not in self.dropped_gyms, gyms) + # Filter ongoing raids + gyms = filter(lambda gym: gym["id"] not in self.raid_gyms, gyms) + # filter fake gyms + # self.gyms = filter(lambda gym: "type" not in gym or gym["type"] != 1, self.gyms) + # sort by current distance + gyms.sort(key=lambda x: distance( + self.bot.position[0], + self.bot.position[1], + x['latitude'], + x['longitude'] + )) + + return gyms + + def get_gyms_in_range(self): + gyms = self.get_gyms() + + if self.bot.config.replicate_gps_xy_noise: + gyms = filter(lambda fort: distance( + self.bot.noised_position[0], + self.bot.noised_position[1], + fort['latitude'], + fort['longitude'] + ) <= Constants.MAX_DISTANCE_FORT_IS_REACHABLE, self.gyms) + else: + gyms = filter(lambda fort: distance( + self.bot.position[0], + self.bot.position[1], + fort['latitude'], + fort['longitude'] + ) <= Constants.MAX_DISTANCE_FORT_IS_REACHABLE, self.gyms) + + return gyms + + def _should_print(self): + return self.next_update is None or datetime.now() >= self.next_update + + def _should_expire(self): + return self.next_expire is None or datetime.now() >= self.next_expire + + def _compute_next_expire(self): + self.next_expire = datetime.now() + timedelta(seconds=300) + + def _compute_next_recheck(self): + wait = uniform(self.min_recheck, self.max_recheck) + self.recheck = datetime.now() + timedelta(seconds=wait) + + def _should_recheck(self): + return self.recheck is None or datetime.now() >= self.recheck + + def _compute_next_update(self): + """ + Computes the next update datetime based on the minimum update interval. + :return: Nothing. + :rtype: None + """ + self.next_update = datetime.now() + timedelta(seconds=self.min_interval) + + def _get_best_pokemon(self, current_pokemons): + def get_poke_info(info, pokemon): + poke_info = { + 'cp': pokemon.cp, + 'iv': pokemon.iv, + 'ivcp': pokemon.ivcp, + 'ncp': pokemon.cp_percent, + 'level': pokemon.level, + 'hp': pokemon.hp, + 'dps': pokemon.moveset.dps + } + if info not in poke_info: + raise ConfigException("order by {}' isn't available".format(self.order_by)) + return poke_info[info] + # Don't place a Pokemon which is already in the gym (prevent ALL Blissey etc) + possible_pokemons = [p for p in self.pokemons if not p.name in current_pokemons] + # Don't put in Pokemon above 3000 cp (morale drops too fast) + possible_pokemons = [p for p in possible_pokemons if p.cp < 3000] + # Filter out "bad" Pokemon + possible_pokemons = [p for p in possible_pokemons if not p.is_bad] + # HP Must be max + possible_pokemons = [p for p in possible_pokemons if p.hp == p.hp_max] + possible_pokemons = [p for p in possible_pokemons if not p.in_fort] + # Sort them + pokemons_ordered = sorted(possible_pokemons, key=lambda x: get_poke_info(self.order_by, x), reverse=True) + # Top 10 picks + pokemons_ordered = pokemons_ordered[0:20] + # Pick a random one! + random.shuffle(pokemons_ordered) + return pokemons_ordered[0] From 5f6b963a90cc6437f666e2476150d8dc097dc53b Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 14:36:52 +0200 Subject: [PATCH 12/38] Enable GymPokemon worker This makes sure that we can use the GymPokemon worker to do things for us. --- pokemongo_bot/cell_workers/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pokemongo_bot/cell_workers/__init__.py b/pokemongo_bot/cell_workers/__init__.py index a08e45da4a..e72eb0d477 100644 --- a/pokemongo_bot/cell_workers/__init__.py +++ b/pokemongo_bot/cell_workers/__init__.py @@ -34,3 +34,4 @@ from .catch_limiter import CatchLimiter from .update_hash_stats import UpdateHashStats from .bad_pokemon import BadPokemon +from .gym_pokemon import GymPokemon \ No newline at end of file From 5953744a09e60275ce9a6cb8bf5c057a586af5a8 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 15:02:20 +0200 Subject: [PATCH 13/38] Making sure that the event of deploying a mon is shown in green --- pokemongo_bot/event_handlers/logging_handler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index c873cc7a0f..62bf2b3ba6 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -128,7 +128,8 @@ class LoggingHandler(EventHandler): 'threw_pokeball': 'none', 'used_lucky_egg': 'none', 'catch_limit_on': 'yellow', - 'catch_limit_off': 'green' + 'catch_limit_off': 'green', + 'deployed_pokemon': 'green' } COLOR_CODE = { 'gray': '\033[90m', From b77af34c2b5787029a45d9c96a598d6657d2e93f Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 15:07:07 +0200 Subject: [PATCH 14/38] Share the feeding event --- pokemongo_bot/cell_workers/gym_pokemon.py | 5 +++++ pokemongo_bot/event_handlers/logging_handler.py | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 638de267de..f6bfb47920 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -406,6 +406,11 @@ def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): if result == 1: # Succesful feeding self.logger.info("Fed a Pokemon!") + self.emit_event( + 'fed_pokemon', + formatted=("We fed %s in the gym %s!!" % (pokemon_id, gym_details["name"])), + data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} + ) else: self.logger.info("Feeding failed! %s" % result) diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index 62bf2b3ba6..138b933de9 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -129,7 +129,8 @@ class LoggingHandler(EventHandler): 'used_lucky_egg': 'none', 'catch_limit_on': 'yellow', 'catch_limit_off': 'green', - 'deployed_pokemon': 'green' + 'deployed_pokemon': 'green', + 'fed_pokemon': 'white' } COLOR_CODE = { 'gray': '\033[90m', From 2e65068ef64354a8c729bbd2eac4ba0fed087594 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 15:26:58 +0200 Subject: [PATCH 15/38] Adding config sample and setting correct base config --- configs/config.json.example | 11 +++++++++++ pokemongo_bot/cell_workers/gym_pokemon.py | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/configs/config.json.example b/configs/config.json.example index 8dc00c4980..b1da623b79 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -481,7 +481,18 @@ "diameter": 4, "step_size": 70 } + }, + { + "type": "GymPokemon", + "config": { + "enabled": false, + "order_by": "cp", + "min_interval":360, + "min_recheck":30, + "max_recheck":120 + } } + ], "map_object_cache_time": 5, "forts": { diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index f6bfb47920..65e643eeae 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -52,7 +52,7 @@ def initialize(self): # 10 seconds from current time self.next_update = datetime.now() + timedelta(0, 10) self.order_by = self.config.get('order_by', 'cp') - self.min_interval = self.config.get('min_interval', 60) + self.min_interval = self.config.get('min_interval', 360) self.min_recheck = self.config.get('min_recheck', 30) self.max_recheck = self.config.get('max_recheck', 120) self.recheck = datetime.now() @@ -671,7 +671,7 @@ def get_poke_info(info, pokemon): possible_pokemons = [p for p in possible_pokemons if not p.in_fort] # Sort them pokemons_ordered = sorted(possible_pokemons, key=lambda x: get_poke_info(self.order_by, x), reverse=True) - # Top 10 picks + # Top 20 picks pokemons_ordered = pokemons_ordered[0:20] # Pick a random one! random.shuffle(pokemons_ordered) From fba2c0421b2d2e8fa2b783d8d2414015dd110d76 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 15:39:10 +0200 Subject: [PATCH 16/38] Update message frequency to be less chatty --- pokemongo_bot/cell_workers/gym_pokemon.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 65e643eeae..4e53d33e08 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -405,7 +405,7 @@ def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): self.logger.info("Feeding: %s" % feeding) if result == 1: # Succesful feeding - self.logger.info("Fed a Pokemon!") + #self.logger.info("Fed a Pokemon!") self.emit_event( 'fed_pokemon', formatted=("We fed %s in the gym %s!!" % (pokemon_id, gym_details["name"])), @@ -523,7 +523,7 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): self.dropped_gyms.append(gym["id"]) gym_details = self.get_gym_details(gym) # SUCCES - self.logger.info("We deployed %s (%s CP) in the gym! We now have %s Pokemon in gyms!" % (fort_pokemon.name, fort_pokemon.cp, len(self.dropped_gyms))) + #self.logger.info("We deployed %s (%s CP) in the gym! We now have %s Pokemon in gyms!" % (fort_pokemon.name, fort_pokemon.cp, len(self.dropped_gyms))) self.emit_event( 'deployed_pokemon', formatted=("We deployed %s (%s CP) in the gym %s!!" % (fort_pokemon.name, fort_pokemon.cp, gym_details["name"])), From 2a19fdb4b063c476ae8d4d00e1f92d6c6ff1ff3b Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 15:53:12 +0200 Subject: [PATCH 17/38] Allow feeding berries from Config file --- configs/config.json.example | 1 + pokemongo_bot/cell_workers/gym_pokemon.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/configs/config.json.example b/configs/config.json.example index b1da623b79..c96daa3a88 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -486,6 +486,7 @@ "type": "GymPokemon", "config": { "enabled": false, + "feed_berries": false, "order_by": "cp", "min_interval":360, "min_recheck":30, diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 4e53d33e08..7bf1765c2c 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -55,6 +55,7 @@ def initialize(self): self.min_interval = self.config.get('min_interval', 360) self.min_recheck = self.config.get('min_recheck', 30) self.max_recheck = self.config.get('max_recheck', 120) + self.feed_berries = self.config.get('feed_berries', False) self.recheck = datetime.now() self.walker = self.config.get('walker', 'StepWalker') self.destination = None @@ -151,7 +152,7 @@ def check_close_gym(self): continue if 'owned_by_team' in gym: if gym["owned_by_team"] == self.team: - # self.feed_pokemons_in_gym(gym) + self.feed_pokemons_in_gym(self,gym) if 'gym_display' in gym: display = gym['gym_display'] @@ -278,7 +279,7 @@ def move_to_destination(self): current_pokemons = self._get_pokemons_in_gym(gym_details) self.drop_pokemon_in_gym(self.destination, current_pokemons) # Feed the Pokemon now we're here... - # self.feed_pokemons_in_gym(self.destination) + self.feed_pokemons_in_gym(self,self.destination) self.destination = None # Look around if there are more gyms to fill self.determin_new_destination() @@ -329,6 +330,10 @@ def _get_pokemons_in_gym(self, gym_details): return pokemon_names def feed_pokemons_in_gym(self, gym): + #Check if berry feeding is enabled from config + if self.feed_berries == False + return True + berries = inventory.items().get(ITEM_RAZZBERRY).count + (inventory.items().get(ITEM_PINAPBERRY).count - 10) + inventory.items().get(ITEM_NANABBERRY).count if berries < 1: self.logger.info("No berries left to feed Pokemon.") From 461cd35dd8d084e93d99af2ed91bbba1de727dc9 Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 16:02:05 +0200 Subject: [PATCH 18/38] Testing the quantity to use for feeding --- pokemongo_bot/cell_workers/gym_pokemon.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 7bf1765c2c..e9b6f2690c 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -360,7 +360,10 @@ def feed_pokemons_in_gym(self, gym): # self.logger.info("This pokemon deserves a candy") berry_id = self._determin_feed_berry_id(motivated_pokemon) poke_id = pokemon_info.get('id') - quantity = pokemon_info.get('num_upgrades') + + #Testing to see what quantity field does. Probably just the amount of berries you want to feed + #quantity = pokemon_info.get('num_upgrades') + quantity=1 self._feed_pokemon(gym, poke_id, berry_id, quantity) def _determin_feed_berry_id(self, motivated_pokemon): From 8d416015403cbbfbf2003aed6d3c32fda62d737b Mon Sep 17 00:00:00 2001 From: goedzo Date: Fri, 14 Jul 2017 21:35:40 +0200 Subject: [PATCH 19/38] Adding support to find gyms in range --- pokemongo_bot/__init__.py | 19 +++++++++++++ pokemongo_bot/cell_workers/gym_pokemon.py | 27 ++++++++++++++++--- .../event_handlers/logging_handler.py | 1 + 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 08b7b208ca..c2959134df 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -1725,6 +1725,25 @@ def get_forts(self, order_by_distance=False): )) return forts + + def get_gyms(self, order_by_distance=False): + forts = [fort + for fort in self.cell['forts'] + if 'latitude' in fort and 'type' not in fort] + # Need to filter out disabled gyms! + forts = filter(lambda x: x["enabled"] is True, forts) + forts = filter(lambda x: 'closed' not in fort, forts) + # forts = filter(lambda x: 'type' not in fort, forts) + + if order_by_distance: + forts.sort(key=lambda x: distance( + self.position[0], + self.position[1], + x['latitude'], + x['longitude'] + )) + + return forts def get_map_objects(self, lat, lng, timestamp, cellid): if time.time() - self.last_time_map_object < self.config.map_object_cache_time: diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index e9b6f2690c..36ea78309d 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -27,11 +27,13 @@ GYM_DETAIL_RESULT_OUT_OF_RANGE = 2 GYM_DETAIL_RESULT_UNSET = 0 +TEAM_NOT_SET = 0 TEAM_BLUE = 1 TEAM_RED = 2 TEAM_YELLOW = 3 TEAMS = { + 0: "Not Set", 1: "Mystic", 2: "Valor", 3: "Instinct" @@ -52,6 +54,7 @@ def initialize(self): # 10 seconds from current time self.next_update = datetime.now() + timedelta(0, 10) self.order_by = self.config.get('order_by', 'cp') + self.enabled = self.config.get('enabled', False) self.min_interval = self.config.get('min_interval', 360) self.min_recheck = self.config.get('min_recheck', 30) self.max_recheck = self.config.get('max_recheck', 120) @@ -69,11 +72,27 @@ def initialize(self): self.check_interval = 0 self.gyms = [] self.raid_gyms = dict() - self.team = self.bot.player_data['team'] + + self.bot.event_manager.register_event('gym_error') + self.bot.event_manager.register_event('fed_pokemon') + self.bot.event_manager.register_event('gym_full') + self.bot.event_manager.register_event('deployed_pokemon') + + #self.logger.info("player_date %s." % self.bot.player_data) + + try: + self.team = self.bot.player_data['team'] + except KeyError: + self.team = TEAM_NOT_SET + if self.enabled: + self.emit_event( + 'gym_error', + formatted="You have no team selected, so the module GymPokemon should be disabled" + ) def should_run(self): - # Check if we have any Pokemons and are level > 5 - return player()._level >= 5 and len(self.pokemons) > 0 + # Check if we have any Pokemons and are level > 5 and have selected a team + return player()._level >= 5 and len(self.pokemons) > 0 and self.team > TEAM_NOT_SET def display_fort_pokemon(self): if len(self.fort_pokemons) == 0: @@ -331,7 +350,7 @@ def _get_pokemons_in_gym(self, gym_details): def feed_pokemons_in_gym(self, gym): #Check if berry feeding is enabled from config - if self.feed_berries == False + if self.feed_berries == False: return True berries = inventory.items().get(ITEM_RAZZBERRY).count + (inventory.items().get(ITEM_PINAPBERRY).count - 10) + inventory.items().get(ITEM_NANABBERRY).count diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index 138b933de9..bb30bf8965 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -130,6 +130,7 @@ class LoggingHandler(EventHandler): 'catch_limit_on': 'yellow', 'catch_limit_off': 'green', 'deployed_pokemon': 'green', + 'gym_error': 'red', 'fed_pokemon': 'white' } COLOR_CODE = { From d17c4e17a69d5beef1a299fb51ee5eb4e2af5586 Mon Sep 17 00:00:00 2001 From: goedzo Date: Sat, 15 Jul 2017 02:49:39 +0200 Subject: [PATCH 20/38] Bugfixes and disabled berry feeding --- configs/config.json.example | 22 ++++---- pokemongo_bot/cell_workers/gym_pokemon.py | 65 +++++++++++++++-------- pokemongo_bot/inventory.py | 3 ++ 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/configs/config.json.example b/configs/config.json.example index c96daa3a88..f73d4beb93 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -464,6 +464,17 @@ "enabled": true } }, + { + "type": "GymPokemon", + "config": { + "enabled": false, + "feed_berries": false, + "order_by": "cp", + "min_interval":360, + "min_recheck":30, + "max_recheck":120 + } + }, { "type": "MoveToFort", "config": { @@ -481,17 +492,6 @@ "diameter": 4, "step_size": 70 } - }, - { - "type": "GymPokemon", - "config": { - "enabled": false, - "feed_berries": false, - "order_by": "cp", - "min_interval":360, - "min_recheck":30, - "max_recheck":120 - } } ], diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 36ea78309d..0042025127 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -97,13 +97,11 @@ def should_run(self): def display_fort_pokemon(self): if len(self.fort_pokemons) == 0: return - self.logger.info("We currently have %s Pokemon in Gym(s)" % len(self.fort_pokemons) ) + self.logger.info("We currently have %s Pokemon in Gym(s):" % len(self.fort_pokemons) ) for pokemon in self.fort_pokemons: lat = self.bot.position[0:2][0] lng = self.bot.position[0:2][1] - details = fort_details(self.bot, pokemon.fort_id, lat, lng) - fort_name = details.get('name', 'Unknown') - self.logger.info("%s: %s (%s CP)" % (fort_name, pokemon.name, pokemon.cp)) + self.logger.info("%s (%s CP)" % (pokemon.name, pokemon.cp)) def work(self): self.pokemons = inventory.pokemons().all() @@ -117,7 +115,7 @@ def work(self): if self._should_print(): self.display_fort_pokemon() self._compute_next_update() - # Do display teh stats about Pokemon in Gym and collection time [please] + # Do display the stats about Pokemon in Gym and collection time [please] if not self.enabled: return WorkerResult.SUCCESS if self.bot.softban: @@ -128,16 +126,12 @@ def work(self): self.logger.info("We have a max of 20 Pokemon in gyms.") return WorkerResult.SUCCESS - if hasattr(self.bot, "hunter_locked_target") and self.bot.hunter_locked_target is not None: - # Don't move to a gym when hunting for a Pokemon + if not self.should_run(): return WorkerResult.SUCCESS - + if self.destination is None: self.check_close_gym() - if not self.should_run(): - return WorkerResult.SUCCESS - if self.destination is None: self.determin_new_destination() @@ -146,6 +140,9 @@ def work(self): # Can return RUNNING to move to a gym return result + if hasattr(self.bot, "hunter_locked_target") and self.bot.hunter_locked_target is not None: + # Don't move to a gym when hunting for a Pokemon + return WorkerResult.SUCCESS return WorkerResult.SUCCESS @@ -171,7 +168,7 @@ def check_close_gym(self): continue if 'owned_by_team' in gym: if gym["owned_by_team"] == self.team: - self.feed_pokemons_in_gym(self,gym) + self.feed_pokemons_in_gym(gym) if 'gym_display' in gym: display = gym['gym_display'] @@ -228,6 +225,11 @@ def determin_new_destination(self): self.logger.info("Gym has %s open spots!" % display['slots_available']) self.destination = gym break + else: + #If there are mons we can feed, check them. Note: gym_details returns false if gym is not in range. + gym_to_feed = self.get_gym_details(gym) + if gym_to_feed!=False: + self.feed_pokemons_in_gym(gym_to_feed) else: # self.logger.info("Found a Neutral gym?") # self.logger.info("Info: %s" % gym) @@ -281,7 +283,7 @@ def move_to_destination(self): } self.emit_event( 'moving_to_fort', - formatted="Moving towards Gym {fort_name} - {distance}", + formatted="Moving towards open Gym {fort_name} - {distance}", data=fort_event_data ) @@ -298,7 +300,7 @@ def move_to_destination(self): current_pokemons = self._get_pokemons_in_gym(gym_details) self.drop_pokemon_in_gym(self.destination, current_pokemons) # Feed the Pokemon now we're here... - self.feed_pokemons_in_gym(self,self.destination) + self.feed_pokemons_in_gym(self.destination) self.destination = None # Look around if there are more gyms to fill self.determin_new_destination() @@ -353,6 +355,10 @@ def feed_pokemons_in_gym(self, gym): if self.feed_berries == False: return True + #check if gym is in range. If not, gym=false + if gym == False: + return True + berries = inventory.items().get(ITEM_RAZZBERRY).count + (inventory.items().get(ITEM_PINAPBERRY).count - 10) + inventory.items().get(ITEM_NANABBERRY).count if berries < 1: self.logger.info("No berries left to feed Pokemon.") @@ -364,9 +370,11 @@ def feed_pokemons_in_gym(self, gym): if gym_info: defenders = gym_info.get('gym_defender', []) for defender in defenders: + #self.logger.info("Feed data: defender %s" % defender) + motivated_pokemon = defender.get('motivated_pokemon') pokemon_info = motivated_pokemon.get('pokemon') - pokemon_id = pokemon_info.get('pokemon_id') + pokemon_id=pokemon_info.get('id') # timestamp when deployed deployed_on = datetime.fromtimestamp(int(motivated_pokemon.get('deploy_ms')) / 1e3) time_deployed = datetime.now() - deployed_on @@ -378,12 +386,18 @@ def feed_pokemons_in_gym(self, gym): # Let's feed this Pokemon a candy # self.logger.info("This pokemon deserves a candy") berry_id = self._determin_feed_berry_id(motivated_pokemon) - poke_id = pokemon_info.get('id') + poke_id = pokemon_id #Testing to see what quantity field does. Probably just the amount of berries you want to feed - #quantity = pokemon_info.get('num_upgrades') - quantity=1 - self._feed_pokemon(gym, poke_id, berry_id, quantity) + #quantity = pokemon_info.get('num_upgrades') <- Failed + #quantity=1 <- Failed + quantity=0 + food_values = motivated_pokemon.get('food_value') + for food_value in food_values: + if food_value.get('food_item') == berry_id: + quantity = food_value.get('motivation_increase') + if quantity !=0: + self._feed_pokemon(gym, poke_id, berry_id, quantity) def _determin_feed_berry_id(self, motivated_pokemon): # # Store the amount of berries we have @@ -416,6 +430,13 @@ def _determin_feed_berry_id(self, motivated_pokemon): return food_values[-1]['food_item'] def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): + + self.logger.info("----THIS IS IN DEVELOPEMENT-----") + self.logger.info("We are feeding pokemon %s with berry %s ",pokemon_id,berry_id) + self.logger.info("Feed data: quantity %s" % quantity) + self.logger.info("----ABORTING HERE SINCE WE HAVE NOT YET FIND THE RIGHT API PARAMETERS-----") + return True + request = self.bot.api.create_request() request.gym_feed_pokemon( starting_quantity=quantity, @@ -466,9 +487,9 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): self.recent_gyms.append(gym["id"]) return WorkerResult.SUCCESS else: - self.logger.info("Empty gym?? %s" % g) + #self.logger.info("Empty gym?? %s" % g) gym_details = self.get_gym_details(gym) - self.logger.info("Details: %s" % gym_details) + #self.logger.info("Details: %s" % gym_details) empty_gym = True if not gym_details or gym_details == {}: self.logger.info("No details for this Gym? Blacklisting!") @@ -515,6 +536,8 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): self.logger.info("Lockout time: %s" % lockout_time.strftime('%Y-%m-%d %H:%M:%S.%f')) while lockout_time > datetime.now(): self.logger.info("Waiting for %s seconds deployment lockout to end..." % (lockout_time-datetime.today()).seconds) + #Feed any mons while we are waiting + self.feed_pokemons_in_gym(gym) if (lockout_time-datetime.today()).seconds > 20: sleep(20) else: diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index 49de951e78..c897961a82 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -999,6 +999,9 @@ def __init__(self, data): self.nickname = self.nickname_raw or self.name self.in_fort = 'deployed_fort_id' in data + if 'deployed_fort_id' in data: + self.fort_id = data['deployed_fort_id'] + 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) From aa306d4df999cae22a91bd2b3ab6d83810fc2770 Mon Sep 17 00:00:00 2001 From: goedzo Date: Sat, 15 Jul 2017 14:10:23 +0200 Subject: [PATCH 21/38] Fix for gyms not sharing their defenders --- pokemongo_bot/cell_workers/gym_pokemon.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 0042025127..230fe1e4fe 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -365,7 +365,14 @@ def feed_pokemons_in_gym(self, gym): return True max_gym_time = timedelta(hours=8,minutes=20) - gym_info = self.get_gym_details(gym).get('gym_status_and_defenders', None) + + try: + gym_info = self.get_gym_details(gym).get('gym_status_and_defenders', None) + except TypeError: + #This gym does not give status results. + self.team = TEAM_NOT_SET + return True + # self.logger.info("Defenders in gym:")okemon_info..items()get('pokemon_id') if gym_info: defenders = gym_info.get('gym_defender', []) From ac151a8549ae4eff0490b9d29ac132100b892dfa Mon Sep 17 00:00:00 2001 From: goedzo Date: Sat, 15 Jul 2017 14:14:04 +0200 Subject: [PATCH 22/38] Stop moving if running fails --- pokemongo_bot/cell_workers/gym_pokemon.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index 230fe1e4fe..b0a07f4f57 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -291,6 +291,9 @@ def move_to_destination(self): if not step_walker.step(): return WorkerResult.RUNNING + else: + #Running fails. Let's stop moving to the gym + return WorkerResult.SUCCESS else: self.emit_event( 'arrived_at_fort', From 4548311f8f705370e74a9e3d5e72c4ea53255e8d Mon Sep 17 00:00:00 2001 From: goedzo Date: Mon, 17 Jul 2017 01:12:53 +0200 Subject: [PATCH 23/38] Gym Deployment finalized --- pokemongo_bot/cell_workers/gym_pokemon.py | 96 ++++++++++++++++------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index b0a07f4f57..ec1ec91549 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -44,6 +44,7 @@ ITEM_PINAPBERRY = 705 + class GymPokemon(BaseTask): SUPPORTED_TASK_API_VERSION = 1 @@ -51,6 +52,10 @@ def __init__(self, bot, config): super(GymPokemon, self).__init__(bot, config) def initialize(self): + + #Adding this to play with finding a quantity that works + self.gym_quantity_test = 1 + # 10 seconds from current time self.next_update = datetime.now() + timedelta(0, 10) self.order_by = self.config.get('order_by', 'cp') @@ -72,7 +77,6 @@ def initialize(self): self.check_interval = 0 self.gyms = [] self.raid_gyms = dict() - self.bot.event_manager.register_event('gym_error') self.bot.event_manager.register_event('fed_pokemon') self.bot.event_manager.register_event('gym_full') @@ -227,9 +231,7 @@ def determin_new_destination(self): break else: #If there are mons we can feed, check them. Note: gym_details returns false if gym is not in range. - gym_to_feed = self.get_gym_details(gym) - if gym_to_feed!=False: - self.feed_pokemons_in_gym(gym_to_feed) + self.feed_pokemons_in_gym(gym) else: # self.logger.info("Found a Neutral gym?") # self.logger.info("Info: %s" % gym) @@ -371,16 +373,15 @@ def feed_pokemons_in_gym(self, gym): try: gym_info = self.get_gym_details(gym).get('gym_status_and_defenders', None) - except TypeError: - #This gym does not give status results. - self.team = TEAM_NOT_SET + except (TypeError,KeyError,AttributeError): + #This gym does not give status results. Probably it is not in range return True # self.logger.info("Defenders in gym:")okemon_info..items()get('pokemon_id') if gym_info: defenders = gym_info.get('gym_defender', []) for defender in defenders: - #self.logger.info("Feed data: defender %s" % defender) + #self.logger.info("LOG: Defender data: defender %s" % defender) motivated_pokemon = defender.get('motivated_pokemon') pokemon_info = motivated_pokemon.get('pokemon') @@ -400,14 +401,16 @@ def feed_pokemons_in_gym(self, gym): #Testing to see what quantity field does. Probably just the amount of berries you want to feed #quantity = pokemon_info.get('num_upgrades') <- Failed - #quantity=1 <- Failed - quantity=0 - food_values = motivated_pokemon.get('food_value') - for food_value in food_values: - if food_value.get('food_item') == berry_id: - quantity = food_value.get('motivation_increase') - if quantity !=0: - self._feed_pokemon(gym, poke_id, berry_id, quantity) + quantity=2 + self._feed_pokemon(gym, poke_id, berry_id, quantity) + + # quantity=0 + # food_values = motivated_pokemon.get('food_value') + # for food_value in food_values: + # if food_value.get('food_item') == berry_id: + # quantity = food_value.get('motivation_increase') + # if quantity !=0: + # self._feed_pokemon(gym, poke_id, berry_id, quantity) def _determin_feed_berry_id(self, motivated_pokemon): # # Store the amount of berries we have @@ -443,13 +446,29 @@ def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): self.logger.info("----THIS IS IN DEVELOPEMENT-----") self.logger.info("We are feeding pokemon %s with berry %s ",pokemon_id,berry_id) - self.logger.info("Feed data: quantity %s" % quantity) + self.logger.info("Feed data: quantity %s" % self.gym_quantity_test) + #self.logger.info("Feed data: gym %s" % gym) self.logger.info("----ABORTING HERE SINCE WE HAVE NOT YET FIND THE RIGHT API PARAMETERS-----") return True - + + #Get the gym_name to show in messages + lat = self.destination["latitude"] + lng = self.destination["longitude"] + + details = fort_details(self.bot, gym["id"], lat, lng) + if details: + gym_name = details.get('name', 'Unknown') + else: + #Seem that we cannot read details here. Let's exit + return True + + + #Overide the quantity to find the right value + #quantity = self.gym_quantity_test + request = self.bot.api.create_request() request.gym_feed_pokemon( - starting_quantity=quantity, + starting_quantity=self.gym_quantity_test, item_id=berry_id, gym_id=gym["id"], pokemon_id=pokemon_id, @@ -459,19 +478,40 @@ def _feed_pokemon(self, gym, pokemon_id, berry_id, quantity): response_dict = request.call() if ('responses' in response_dict) and ('GYM_FEED_POKEMON' in response_dict['responses']): feeding = response_dict['responses']['GYM_FEED_POKEMON'] + self.logger.info("Feeding result: %s" % feeding) result = feeding.get('result', -1) - self.logger.info("Feeding: %s" % feeding) if result == 1: # Succesful feeding - #self.logger.info("Fed a Pokemon!") self.emit_event( 'fed_pokemon', - formatted=("We fed %s in the gym %s!!" % (pokemon_id, gym_details["name"])), + formatted=("We fed %s in the gym %s!!" % (pokemon_id, gym_name)), data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} ) + elif result == 4: #ERROR_POKEMON_NOT_THERE + self.emit_event( + 'fed_pokemon', + formatted=("Pokemon %s has dropped out of the gym %s!" % (pokemon_id, gym_name)), + data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} + ) + elif result == 5: #ERROR_POKEMON_FULL + self.emit_event( + 'fed_pokemon', + formatted=("Pokemon %s is full in gym %s!" % (pokemon_id, gym_name)), + data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} + ) + elif result == 7: #ERROR_WRONG_TEAM + self.emit_event( + 'fed_pokemon', + formatted=("Team Switched while feeding %s in gym %s!" % (pokemon_id, gym_name)), + data={'gym_id': gym['id'], 'pokemon_id': pokemon_id} + ) + elif result == 8: #ERROR_WRONG_COUNT + #Let's try a different quantity + self.gym_quantity_test += 1 else: self.logger.info("Feeding failed! %s" % result) - + + def drop_pokemon_in_gym(self, gym, current_pokemons): self.pokemons = inventory.pokemons().all() self.fort_pokemons = [p for p in self.pokemons if p.in_fort] @@ -546,12 +586,12 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): self.logger.info("Lockout time: %s" % lockout_time.strftime('%Y-%m-%d %H:%M:%S.%f')) while lockout_time > datetime.now(): self.logger.info("Waiting for %s seconds deployment lockout to end..." % (lockout_time-datetime.today()).seconds) - #Feed any mons while we are waiting - self.feed_pokemons_in_gym(gym) - if (lockout_time-datetime.today()).seconds > 20: - sleep(20) + if (lockout_time-datetime.today()).seconds > 40: + sleep(40) + #Feed any mons while we are waiting + self.feed_pokemons_in_gym(gym) else: - sleep((lockout_time-t).seconds) + sleep((lockout_time-datetime.today()).seconds) break #FortDeployPokemon From 36acea47521b713da8b617a415eb261c1a417084 Mon Sep 17 00:00:00 2001 From: goedzo Date: Mon, 17 Jul 2017 01:27:49 +0200 Subject: [PATCH 24/38] Fixes issue with newer version of docker #6063 --- Dockerfile | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Dockerfile b/Dockerfile index b5bdc16128..a47c67f76d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,17 +26,24 @@ RUN apk -U --no-cache add python py-pip tzdata \ && rm -rf /var/cache/apk/* \ && find / -name '*.pyc' -o -name '*.pyo' | xargs -rn1 rm -f -ADD http://pgoapi.com/pgoencrypt.tar.gz /tmp/pgoencrypt.tar.gz ADD https://raw.githubusercontent.com/$BUILD_REPO/$BUILD_BRANCH/requirements.txt . -RUN apk -U --no-cache add --virtual .build-dependencies python-dev gcc make musl-dev git \ - && tar zxf /tmp/pgoencrypt.tar.gz -C /tmp \ - && make -C /tmp/pgoencrypt/src \ - && cp /tmp/pgoencrypt/src/libencrypt.so /usr/src/app/encrypt.so \ - && ln -s locale.h /usr/include/xlocale.h \ - && pip install --no-cache-dir -r requirements.txt \ - && apk del .build-dependencies \ - && rm -rf /var/cache/apk/* /tmp/pgoencrypt* /usr/include/xlocale.h \ - && find / -name '*.pyc' -o -name '*.pyo' | xargs -rn1 rm -f + +#Need to load cert for WGET +RUN apk update +RUN apk add ca-certificates wget +RUN update-ca-certificates +RUN wget -P /tmp/ http://pgoapi.com/pgoencrypt.tar.gz + +RUN apk -U --no-cache add --virtual .build-dependencies python-dev gcc make musl-dev git +RUN tar xvzf /tmp/pgoencrypt.tar.gz -C /tmp +RUN make -C /tmp/pgoencrypt/src +RUN cp /tmp/pgoencrypt/src/libencrypt.so /usr/src/app/encrypt.so +RUN ln -s locale.h /usr/include/xlocale.h +RUN pip install --no-cache-dir -r requirements.txt +RUN apk del .build-dependencies +RUN rm -rf /var/cache/apk/* /tmp/pgoencrypt* /usr/include/xlocale.h +RUN find / -name '*.pyc' -o -name '*.pyo' | xargs -rn1 rm -f + ADD https://api.github.com/repos/$BUILD_REPO/commits/$BUILD_BRANCH /tmp/pgobot-version RUN apk -U --no-cache add --virtual .pgobot-dependencies wget ca-certificates tar jq \ From d61ad5d071046b44888e180d0a0ae13567ee77be Mon Sep 17 00:00:00 2001 From: goedzo Date: Mon, 17 Jul 2017 01:29:30 +0200 Subject: [PATCH 25/38] Fixes issue with newer version of docker #6063 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f76d0fc5f5..d517615426 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.11.0 networkx==1.11 six==1.10 -git+https://github.com/goedzo/pgoapi.git@develop#egg=pgoapi +git+https://github.com/pogodevorg/pgoapi.git@develop#egg=pgoapi geopy==1.11.0 geographiclib==1.46.3 requests==2.10.0 From 78d3cc185530ad133002871d0d2b526853379414 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Mon, 17 Jul 2017 12:47:03 +0200 Subject: [PATCH 26/38] Introducing HealPokemon HealPokemon will try to use the potions and revives to heal and revive your Pokemon This does so without any filtering. Usefull when you get home from a raid and dont want to heal/rivive all the Pokemon yourself. This will try to do so as effectently as possible --- docs/configuration_files.md | 30 +++ pokemongo_bot/cell_workers/__init__.py | 1 + pokemongo_bot/cell_workers/heal_pokemon.py | 251 +++++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 pokemongo_bot/cell_workers/heal_pokemon.py diff --git a/docs/configuration_files.md b/docs/configuration_files.md index dacf3a507e..853bfce16d 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -63,6 +63,7 @@ - [BuddyPokemon](#buddypokemon) - [PokemonHunter](#pokemonhunter) - [BadPokemon](#badpokemon) +- [HealPokemon](#healpokemon) # Configuration files @@ -1486,3 +1487,32 @@ If you have any Pokemon that Niantic has marked as bad (red slashes) this will n } } ``` + +## HealPokemon +[[back to top](#table-of-contents)] + +### Description +[[back to top](#table-of-contents)] + +If you have any Pokemon that are dead or need healing, this task will try to do that. + +### Options +[[back to top](#table-of-contents)] + +* `heal`: `Default: True`. Should Pokemon be healed? +* `revive`: `Default: True`. Should dead Pokemon be revived? + + +### Sample configuration +[[back to top](#table-of-contents)] +```json +{ + "type": "BadPokemon", + "config": { + "enabled": true, + "heal": true, + "revive": true + } +} +``` + diff --git a/pokemongo_bot/cell_workers/__init__.py b/pokemongo_bot/cell_workers/__init__.py index a08e45da4a..57fa324ae8 100644 --- a/pokemongo_bot/cell_workers/__init__.py +++ b/pokemongo_bot/cell_workers/__init__.py @@ -34,3 +34,4 @@ from .catch_limiter import CatchLimiter from .update_hash_stats import UpdateHashStats from .bad_pokemon import BadPokemon +from .heal_pokemon import HealPokemon diff --git a/pokemongo_bot/cell_workers/heal_pokemon.py b/pokemongo_bot/cell_workers/heal_pokemon.py new file mode 100644 index 0000000000..c5cde80688 --- /dev/null +++ b/pokemongo_bot/cell_workers/heal_pokemon.py @@ -0,0 +1,251 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals +from __future__ import absolute_import + +from datetime import datetime, timedelta +from pokemongo_bot.base_task import BaseTask +from pokemongo_bot.worker_result import WorkerResult +from pokemongo_bot import inventory +from pokemongo_bot.item_list import Item +from pokemongo_bot.human_behaviour import sleep, action_delay + +class HealPokemon(BaseTask): + SUPPORTED_TASK_API_VERSION = 1 + + def __init__(self, bot, config): + super(HealPokemon, self).__init__(bot, config) + self.bot = bot + self.config = config + self.enabled = self.config.get("enabled", False) + self.revive_pokemon = self.config.get("revive", True) + self.heal_pokemon = self.config.get("heal", True) + self.next_update = None + self.to_heal = [] + + def work(self): + + if not self.enabled: + return WorkerResult.SUCCESS + + # Check for pokemon to heal or revive + to_revive = [] + self.to_heal = [] + pokemons = inventory.pokemons().all() + pokemons.sort(key=lambda p: p.hp) + for pokemon in pokemons: + if pokemon.hp < 1.0: + self.logger.info("Dead: %s (%s CP| %s/%s )" % (pokemon.name, pokemon.cp, pokemon.hp, pokemon.hp_max)) + to_revive += [pokemon] + elif pokemon.hp < pokemon.hp_max: + self.logger.info("Heal: %s (%s CP| %s/%s )" % (pokemon.name, pokemon.cp, pokemon.hp, pokemon.hp_max)) + self.to_heal += [pokemon] + + if len(self.to_heal) == 0 and len(to_revive) == 0: + if self._should_print: + self.next_update = datetime.now() + timedelta(seconds=120) + #self.logger.info("No pokemon to heal or revive") + return WorkerResult.SUCCESS + # Okay, start reviving pokemons + # Check revives and potions + revives = inventory.items().get(Item.ITEM_REVIVE.value).count + max_revives = inventory.items().get(Item.ITEM_MAX_REVIVE.value).count + 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 + + self.logger.info("Healing %s pokemon" % len(self.to_heal)) + self.logger.info("Reviving %s pokemon" % len(to_revive)) + + if self.revive_pokemon: + if len(to_revive) > 0 and revives == 0 and max_revives == 0: + self.logger.info("No revives left! Can't revive %s pokemons." % len(to_revive)) + elif len(to_revive) > 0: + self.logger.info("Reviving %s pokemon..." % len(to_revive)) + for pokemon in to_revive: + self._revive_pokemon(pokemon) + + if self.heal_pokemon: + if len(self.to_heal) > 0 and (normal + super_p + hyper + max_p) == 0: + self.logger.info("No potions left! Can't heal %s pokemon" % len(self.to_heal)) + elif len(self.to_heal) > 0: + self.logger.info("Healing %s pokemon" % len(self.to_heal)) + for pokemon in self.to_heal: + self._heal_pokemon(pokemon) + + if self._should_print: + self.next_update = datetime.now() + timedelta(seconds=120) + self.logger.info("Done healing/reviving pokemon") + + 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 _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 + + + def _use_potion(self, potion_id, pokemon): + potion_count = inventory.items().get(potion_id).count + healing = 0 + if potion_count == 0: + return False + if potion_id == 101: + self.logger.info("Healing with a normal potion we have %s left." % (potion_count - 1)) + healing = 20 + if potion_id == 102: + self.logger.info("Healing with a Super potion we have %s left." % (potion_count - 1)) + healing = 50 + if potion_id == 103: + self.logger.info("Healing with a HYper potion we have %s left." % (potion_count - 1)) + healing = 200 + if potion_id == 104: + self.logger.info("Healing with a MAX potion we have %s left." % (potion_count - 1)) + healing = pokemon.hp_max - pokemon.hp + + response_dict_potion = self.bot.api.use_item_potion(item_id=potion_id, pokemon_id=pokemon.unique_id) + # Select potion + sleep(2) + if response_dict_potion: + result = response_dict_potion.get('responses', {}).get('USE_ITEM_POTION', {}).get('result', 0) + if result is 1 or result is 0: # Request success + potion_item = inventory.items().get(potion_id) + # Remove the potion from the iventory + potion_item.remove(1) + self.emit_event( + 'healing_pokemon', + formatted='Healing {name} ({hp} -> {hp_new}/{hp_max}).', + data={ + 'name': pokemon.name, + 'hp': pokemon.hp, + 'hp_new': pokemon.hp + healing, + 'hp_max': pokemon.hp_max + } + ) + return True + elif result == 3: + # ERROR_CANNOT_USE + pokemon.hp = pokemon.hp_max + self.logger.info("Can't use this to heal the %s" % pokemon.name) + return False + else: + self.logger.info("Result was: %s" % result) + self.emit_event( + 'healing_pokemon', + level='error', + formatted='Failed to heal {name} ({hp} -> {hp_new}/{hp_max})!', + data={ + 'name': pokemon.name, + 'hp': pokemon.hp, + 'hp_new': pokemon.hp + healing, + 'hp_max': pokemon.hp_max + } + ) + return False + + def _should_print(self): + """ + Returns a value indicating whether the pokemon should be displayed. + :return: True if the stats should be displayed; otherwise, False. + :rtype: bool + """ + return self.next_update is None or datetime.now() >= self.next_update From 9a9b7a6c05bc00efada77b3e243b4474bf46dff6 Mon Sep 17 00:00:00 2001 From: goedzo Date: Mon, 17 Jul 2017 15:58:15 +0200 Subject: [PATCH 27/38] Full API Support for 0.67.2 based on latest compile from POGOProtos Recompiled latest https://github.com/AeonLucid/POGOProtos API and added to my own maintained repository. fixes #6075 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index d517615426..f76d0fc5f5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ numpy==1.11.0 networkx==1.11 six==1.10 -git+https://github.com/pogodevorg/pgoapi.git@develop#egg=pgoapi +git+https://github.com/goedzo/pgoapi.git@develop#egg=pgoapi geopy==1.11.0 geographiclib==1.46.3 requests==2.10.0 From 432a51c3908b1148b33a61c431fb740e58bf3d9f Mon Sep 17 00:00:00 2001 From: goedzo Date: Tue, 18 Jul 2017 00:56:34 +0200 Subject: [PATCH 28/38] Fixed Heal Pokemon Event "revived_pokemon" was not registerd. --- pokemongo_bot/__init__.py | 1 + pokemongo_bot/event_handlers/logging_handler.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 08b7b208ca..3095b34224 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -559,6 +559,7 @@ def _register_events(self): self.event_manager.register_event('catch_limit') self.event_manager.register_event('spin_limit') self.event_manager.register_event('show_best_pokemon', parameters=('pokemons')) + self.event_manager.register_event('revived_pokemon') # level up stuff self.event_manager.register_event( diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index c873cc7a0f..ce07aeb6c1 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -128,7 +128,8 @@ class LoggingHandler(EventHandler): 'threw_pokeball': 'none', 'used_lucky_egg': 'none', 'catch_limit_on': 'yellow', - 'catch_limit_off': 'green' + 'catch_limit_off': 'green', + 'revived_pokemon': 'green' } COLOR_CODE = { 'gray': '\033[90m', From de9cb21f590331c4870fb1d9ce3635a114bd2f99 Mon Sep 17 00:00:00 2001 From: goedzo Date: Tue, 18 Jul 2017 01:08:50 +0200 Subject: [PATCH 29/38] Fixed all Heal Pokemon bugs --- pokemongo_bot/__init__.py | 1 + pokemongo_bot/event_handlers/logging_handler.py | 4 +++- pokemongo_bot/inventory.py | 5 +++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index 3095b34224..3e8bd41a67 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -560,6 +560,7 @@ def _register_events(self): self.event_manager.register_event('spin_limit') self.event_manager.register_event('show_best_pokemon', parameters=('pokemons')) self.event_manager.register_event('revived_pokemon') + self.event_manager.register_event('healing_pokemon') # level up stuff self.event_manager.register_event( diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index ce07aeb6c1..86c9fa5a92 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -129,7 +129,9 @@ class LoggingHandler(EventHandler): 'used_lucky_egg': 'none', 'catch_limit_on': 'yellow', 'catch_limit_off': 'green', - 'revived_pokemon': 'green' + 'revived_pokemon': 'green', + 'healing_pokemon': 'green' + } COLOR_CODE = { 'gray': '\033[90m', diff --git a/pokemongo_bot/inventory.py b/pokemongo_bot/inventory.py index 49de951e78..e7a5129e4b 100644 --- a/pokemongo_bot/inventory.py +++ b/pokemongo_bot/inventory.py @@ -522,6 +522,11 @@ def add(self, pokemon): raise ValueError("Pokemon already present in the inventory") self._data[pokemon.unique_id] = pokemon + def get_from_unique_id(self, pokemon_unique_id): + if pokemon_unique_id not in self._data: + raise ValueError("Pokemon not present in the inventory") + return self._data[pokemon_unique_id] + def remove(self, pokemon_unique_id): if pokemon_unique_id not in self._data: raise ValueError("Pokemon not present in the inventory") From d6bcbd592917de415e6dc9a52d355840cb213ee9 Mon Sep 17 00:00:00 2001 From: goedzo Date: Tue, 18 Jul 2017 02:02:32 +0200 Subject: [PATCH 30/38] Making sure we are all fixed now --- pokemongo_bot/event_handlers/logging_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index 08f2884442..226530f9f0 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -133,7 +133,7 @@ class LoggingHandler(EventHandler): 'healing_pokemon': 'green', 'deployed_pokemon': 'green', 'gym_error': 'red', - 'fed_pokemon': 'white + 'fed_pokemon': 'white' } COLOR_CODE = { 'gray': '\033[90m', From 6ed8089a535f67f13823d13b292db04ba29a8e1f Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 08:57:12 +0200 Subject: [PATCH 31/38] Changes to spinning Pokestops and Gyms --- pokemongo_bot/cell_workers/spin_fort.py | 66 +++++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/pokemongo_bot/cell_workers/spin_fort.py b/pokemongo_bot/cell_workers/spin_fort.py index e47d20f1a7..5712dd27e0 100644 --- a/pokemongo_bot/cell_workers/spin_fort.py +++ b/pokemongo_bot/cell_workers/spin_fort.py @@ -19,6 +19,7 @@ SPIN_REQUEST_RESULT_OUT_OF_RANGE = 2 SPIN_REQUEST_RESULT_IN_COOLDOWN_PERIOD = 3 SPIN_REQUEST_RESULT_INVENTORY_FULL = 4 +SPIN_REQUEST_RESULT_POI_INACCESSIBLE = 5 LURE_REQUEST_RESULT_SUCCESS = 1 LURE_REQUEST_FORT_ALREADY_HAS_MODIFIER= 2 @@ -35,6 +36,8 @@ def __init__(self, bot, config): def initialize(self): # 10 seconds from current time self.next_update = datetime.now() + timedelta(0, 10) + self.fort_spins = 0 + self.streak_forts = [] self.ignore_item_count = self.config.get("ignore_item_count", False) self.spin_wait_min = self.config.get("spin_wait_min", 2) @@ -42,6 +45,10 @@ def initialize(self): self.min_interval = int(self.config.get('min_interval', 120)) self.exit_on_limit_reached = self.config.get("exit_on_limit_reached", True) self.use_lure = self.config.get("use_lure", False) + self.try_to_keep_streak = self.config.get("try_to_keep_streak", True) + + # if self.try_to_keep_streak and len(self.bot.recent_forts) is not 10: + # self.logger.warn("You enabled the setting for keeping a 10 stop streak, but the number of recent forts is not set to 10! It is set to %s. This will cause the streak to fail!" % len(self.bot.recent_forts)) def should_run(self): has_space_for_loot = inventory.Items.has_space_for_loot() @@ -74,27 +81,36 @@ def work(self): 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') + fort_name = details.get('name', 'Unknown') check_fort_modifier = details.get('modifiers', {}) - if check_fort_modifier: + 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]), + fort_id = fort['id'], + player_latitude = f2i(self.bot.position[0]), player_longitude = f2i(self.bot.position[1]) ) response_dict = request.call() @@ -114,7 +130,7 @@ def work(self): 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'], @@ -124,7 +140,7 @@ def work(self): 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) @@ -134,20 +150,39 @@ def work(self): 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 pokestop {pokestop}. Experience awarded: {exp}. Items awarded: {items}", + 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 } ) @@ -196,6 +231,9 @@ def work(self): 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', @@ -224,7 +262,7 @@ def work(self): result = c.fetchone() if result[0] == 1: - source = str("PokemonCatchWorker") + source = str("SpinFort") status = str("Possible Softban") conn.execute('''INSERT INTO softban_log (status, source) VALUES (?, ?)''', (status, source)) else: @@ -246,6 +284,14 @@ def work(self): def get_forts_in_range(self): forts = self.bot.get_forts(order_by_distance=True) forts = filter(lambda fort: fort["id"] not in self.bot.fort_timeouts, forts) + if self.bot.camping_forts and self.try_to_keep_streak: + if datetime.now() >= self.next_update: + self.logger.info("Camping forts, ignoring 10 stops streak.") + elif self.try_to_keep_streak: + if len(self.streak_forts) > 10: + self.streak_forts.pop() + # Remove all forts which were spun in the last ticks to keep 10 stops streak + forts = filter(lambda x: x["id"] not in self.streak_forts, forts) if self.bot.config.replicate_gps_xy_noise: forts = filter(lambda fort: distance( From 424bae09c6d3146ca1e5db702be1f69ad8696509 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 08:58:39 +0200 Subject: [PATCH 32/38] Trying to solve move to fort issues Inspired on PokemonHunter, when having difficulties moving try to change the walker and if it doesnt help, ignore the stop --- pokemongo_bot/cell_workers/move_to_fort.py | 57 ++++++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/pokemongo_bot/cell_workers/move_to_fort.py b/pokemongo_bot/cell_workers/move_to_fort.py index 40c4d68658..24a65523b8 100644 --- a/pokemongo_bot/cell_workers/move_to_fort.py +++ b/pokemongo_bot/cell_workers/move_to_fort.py @@ -9,6 +9,7 @@ from pokemongo_bot.base_task import BaseTask from .utils import distance, format_dist, fort_details from datetime import datetime, timedelta +import time class MoveToFort(BaseTask): SUPPORTED_TASK_API_VERSION = 1 @@ -22,6 +23,8 @@ def initialize(self): self.walker = self.config.get('walker', 'StepWalker') self.wait_at_fort = self.config.get('wait_on_lure', False) self.wait_log_sent = None + self.previous_distance = [] + self.target_id = None def should_run(self): has_space_for_loot = inventory.Items.has_space_for_loot() @@ -39,6 +42,9 @@ def work(self): if not self.should_run(): return WorkerResult.SUCCESS + if hasattr(self.bot, "hunter_locked_target") and self.bot.hunter_locked_target is not None: + return WorkerResult.SUCCESS + nearest_fort = self.get_nearest_fort() if nearest_fort is None: @@ -50,6 +56,9 @@ def work(self): details = fort_details(self.bot, fortID, lat, lng) fort_name = details.get('name', 'Unknown') + if self.target_id is None: + self.target_id = fort_name + unit = self.bot.config.distance_unit # Unit to use when printing formatted distance dist = distance( @@ -67,24 +76,64 @@ def work(self): moving = noised_dist > Constants.MAX_DISTANCE_FORT_IS_REACHABLE if self.bot.config.replicate_gps_xy_noise else dist > Constants.MAX_DISTANCE_FORT_IS_REACHABLE + distance_to_target = int(noised_dist if self.bot.config.replicate_gps_xy_noise else dist) + if len(self.previous_distance) == 0: + self.previous_distance.append(distance_to_target) + elif self.target_id is not fort_name: + # self.logger.info("Changed target from %s to %s" % (self.target_id, fort_name)) + self.previous_distance = [distance_to_target] + self.target_id = fort_name + if self.walker is not self.config.get('walker', 'StepWalker'): + self.walker = self.config.get('walker', 'StepWalker') + else: + # self.logger.info("Previous distances: %s" % self.previous_distance) + if len(self.previous_distance) > 5: + self.previous_distance.pop(0) + error_moving = False + times_found = 0 + for prev_distance in self.previous_distance: + if prev_distance == distance_to_target: + error_moving = True + break + + if error_moving: + if self.walker == 'StepWalker': + self.logger.info("Having difficulty walking to %s" % fort_name) + self.bot.recent_forts = self.bot.recent_forts[1:] + [fortID] + return WorkerResult.ERROR + else: + self.logger.info("Having difficulty walking to %s. Changing walker." % fort_name) + self.walker = 'StepWalker' + self.previous_distance = [distance_to_target] + else: + self.previous_distance.append(distance_to_target) + if moving: self.wait_log_sent = None + if "type" in nearest_fort and nearest_fort["type"] == 1: + # It's a Pokestop + target_type = "pokestop" + else: + # It's a gym + target_type = "gym" + fort_event_data = { 'fort_name': u"{}".format(fort_name), 'distance': format_dist(dist, unit), + 'target_type': target_type, } if self.is_attracted() > 0: fort_event_data.update(lure_distance=format_dist(self.lure_distance, unit)) self.emit_event( 'moving_to_lured_fort', - formatted="Moving towards pokestop {fort_name} - {distance} (attraction of lure {lure_distance})", + formatted="Moving towards {target_type} {fort_name} - {distance} (attraction of lure {lure_distance})", data=fort_event_data ) else: self.emit_event( 'moving_to_fort', - formatted="Moving towards pokestop {fort_name} - {distance}", + formatted="Moving towards {target_type} {fort_name} - {distance}", data=fort_event_data ) @@ -178,7 +227,7 @@ def get_nearest_fort(self): if len(forts) >= 3: # Get ID of fort, store it. Check index 0 & index 2. Both must not be same nearest_fort = forts[0] - + if len(self.fort_ids) < 3: self.fort_ids.extend(nearest_fort['id']) else: @@ -191,7 +240,7 @@ def get_nearest_fort(self): else: self.fort_ids.pop(0) self.fort_ids.extend(nearest_fort['id']) - + return nearest_fort else: return None From 8241825dfe74ed132ad2e1c5c060a616998245eb Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 09:04:52 +0200 Subject: [PATCH 33/38] Caching of forts small adjustment When a fort is too far away, dont try to get the details. When it is in range do --- pokemongo_bot/cell_workers/utils.py | 32 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/pokemongo_bot/cell_workers/utils.py b/pokemongo_bot/cell_workers/utils.py index b0112a0958..25a8f6a9ce 100644 --- a/pokemongo_bot/cell_workers/utils.py +++ b/pokemongo_bot/cell_workers/utils.py @@ -26,8 +26,28 @@ def fort_details(bot, fort_id, latitude, longitude): """ Lookup fort metadata and (if possible) serve from cache. """ - + first_call = False if fort_id not in FORT_CACHE: + if distance(latitude, longitude, bot.position[0], bot.position[1]) > 1000: + # Fort too far away to get the details! + FORT_CACHE[fort_id] = dict() + first_call = True + else: + """ + Lookup the fort details and cache the response for future use. + """ + request = bot.api.create_request() + request.fort_details(fort_id=fort_id, latitude=latitude, longitude=longitude) + try: + response_dict = request.call() + FORT_CACHE[fort_id] = response_dict['responses']['FORT_DETAILS'] + first_call = True + except Exception: + FORT_CACHE[fort_id] = dict() + first_call = True + + if not first_call and FORT_CACHE.get(fort_id, dict()) == dict(): + if distance(latitude, longitude, bot.position[0], bot.position[1]) < 1000: """ Lookup the fort details and cache the response for future use. """ @@ -37,7 +57,7 @@ def fort_details(bot, fort_id, latitude, longitude): response_dict = request.call() FORT_CACHE[fort_id] = response_dict['responses']['FORT_DETAILS'] except Exception: - pass + FORT_CACHE[fort_id] = dict() # Just to avoid KeyErrors return FORT_CACHE.get(fort_id, {}) @@ -133,14 +153,14 @@ def getSeconds(strTime): try: x = dt.strptime(strTime, '%H:%M:%S') seconds = int(timedelta(hours=x.hour,minutes=x.minute,seconds=x.second).total_seconds()) - except ValueError: + except ValueError: seconds = 0; - + if seconds < 0: seconds = 0; - + return seconds - + def format_time(seconds): # Return a string displaying the time given as seconds or minutes num, duration = 0, long(round(seconds)) From 2f9ab1760e83d63ec63028b0f8ffd01f25955ad9 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 09:07:55 +0200 Subject: [PATCH 34/38] Removed unused config unused config should not be in example config --- configs/config.json.example | 1 - 1 file changed, 1 deletion(-) diff --git a/configs/config.json.example b/configs/config.json.example index f73d4beb93..71d098d45a 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -468,7 +468,6 @@ "type": "GymPokemon", "config": { "enabled": false, - "feed_berries": false, "order_by": "cp", "min_interval":360, "min_recheck":30, From db9f99f2a1a34413ae0f68d9e9d7dd6a1dd800ab Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 09:08:42 +0200 Subject: [PATCH 35/38] Removed unused event Event is gone, no need to color that --- pokemongo_bot/event_handlers/logging_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pokemongo_bot/event_handlers/logging_handler.py b/pokemongo_bot/event_handlers/logging_handler.py index 226530f9f0..22db8f75fe 100644 --- a/pokemongo_bot/event_handlers/logging_handler.py +++ b/pokemongo_bot/event_handlers/logging_handler.py @@ -132,8 +132,7 @@ class LoggingHandler(EventHandler): 'revived_pokemon': 'green', 'healing_pokemon': 'green', 'deployed_pokemon': 'green', - 'gym_error': 'red', - 'fed_pokemon': 'white' + 'gym_error': 'red' } COLOR_CODE = { 'gray': '\033[90m', From 38d488d302a15aceb7388c02cb0e5483eafb423a Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 10:53:29 +0200 Subject: [PATCH 36/38] Fixing missed events --- pokemongo_bot/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index b51901fc3e..769b61f47d 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -372,6 +372,7 @@ def _register_events(self): 'moving_to_fort', parameters=( 'fort_name', + 'target_type', 'distance' ) ) @@ -379,6 +380,7 @@ def _register_events(self): 'moving_to_lured_fort', parameters=( 'fort_name', + 'target_type', 'distance', 'lure_distance' ) @@ -386,7 +388,7 @@ def _register_events(self): self.event_manager.register_event( 'spun_pokestop', parameters=( - 'pokestop', 'exp', 'items' + 'pokestop', 'exp', 'items', 'stop_kind', 'spin_amount_now' ) ) self.event_manager.register_event( From b633a1ff68f8d47b39af00e00c1c4812af56ae5f Mon Sep 17 00:00:00 2001 From: Jhero Date: Tue, 18 Jul 2017 12:39:37 +0200 Subject: [PATCH 37/38] Update README.md --- README.md | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index e4aaa79e22..db575b8ed2 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ If you need any help please don't create an issue as we have a great community o - #dev channel in slack ## Discord - - [Click here to join discord server](https://discord.gg/n3g5puF) + - [Click here to join discord server](https://discord.gg/n3g5puF) ---->RECOMENDED<---- ### [Bugs / Issues](https://github.com/PokemonGoF/PokemonGo-Bot/issues?q=is%3Aissue+sort%3Aupdated-desc) If you discover a bug in the bot, please [search our issue tracker](https://github.com/PokemonGoF/PokemonGo-Bot/issues?q=is%3Aissue+sort%3Aupdated-desc) first. If it hasn't been reported, please [create a new issue](https://github.com/PokemonGoF/PokemonGo-Bot/issues/new) and ensure you follow the template guide so that our team can assist you as quickly as possible. @@ -53,27 +53,25 @@ If this is your first time making a PR or aren't sure of the standard practice o - [x] Search Pokestops - [x] Catch Pokemon - [x] Determine which pokeball to use (uses Razz Berry if the catch percentage is low!) -- [x] Exchange Pokemon as per configuration -- [x] Evolve Pokemon as per configuration +- [x] Exchange, evolve and catch Pokemon as per configuration +- [x] Transfer Pokemon in bulk - [x] Auto switch mode (Inventory Checks - switches between catch/farming items) - [x] Limit the step to farm specific area for pokestops -- [x] Rudimentary IV Functionality filter -- [x] Ignore certain pokemon filter +- [x] Limit Pokestops/catch Pokemons per day +- [x] IV Functionality filter - [x] Adjust delay between Pokemon capture & Transfer as per configuration - [x] Hatch eggs - [x] Incubate eggs -- [x] Crowd Sourced Map Prototype -- [ ] [Standalone Desktop Application] (https://github.com/PokemonGoF/PokemonGo-Bot-Desktop) - [x] Use candy +- [x] Set family ID as VIP and priorice bot to catch it! +- [x] Spin Gyms pokestops +- [x] Transfer red slashed pokemons +- [x] Set shiny pokemons as VIP +- [x] Deploy a pokemon in Gym if slot available +- [x] Docker support +- [x] Auto heal Pokemons +- [x] Information about PGoAPI bot version is rather Bossland endpoint, expiration key date and RPM used -## Analytics -[PokemonGo-Bot](https://github.com/PokemonGoF/PokemonGo-Bot) is very popular and has a vibrant community. Because of that, it has become very difficult for us to know how the bot is used and what errors people hit. By capturing small amounts of data, we can prioritize our work better such as fixing errors that happen to a large percentage of our user base, not just a vocal minority. - -Our goal is to help inform our decisions by capturing data that helps us get aggregate usage and error reports, not personal information. To view the code that handles analytics in our master branch, you can use this [search link](https://github.com/PokemonGoF/PokemonGo-Bot/search?utf8=%E2%9C%93&q=BotEvent). - -If there are any concerns with this policy or you believe we are tracking something we shouldn't, please open a ticket in the tracker. The contributors always intend to do the right thing for our users, and we want to make sure we are held to that path. - -If you do not want any data to be gathered, you can turn off this feature by setting `health_record` to `false` in your `config.json`. ## Credits - [tejado](https://github.com/tejado) many thanks for the API @@ -167,6 +165,14 @@ If you do not want any data to be gathered, you can turn off this feature by set * Gobberwart * javajohnHub * kolinkorr839 + * lepeli + * davidakachaos + * MerlionRock + * walaoaaa1234 + * pogarek + * goedzo + * solderzzc aka BIG BOSS + ## Disclaimer ©2016 Niantic, Inc. ©2016 Pokémon. ©1995–2016 Nintendo / Creatures Inc. / GAME FREAK inc. © 2016 Pokémon/Nintendo Pokémon and Pokémon character names are trademarks of Nintendo. The Google Maps Pin is a trademark of Google Inc. and the trade dress in the product design is a trademark of Google Inc. under license to The Pokémon Company. Other trademarks are the property of their respective owners. From 826d86abb756e9fc4df024f510616a7f36401353 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 18 Jul 2017 19:32:17 +0200 Subject: [PATCH 38/38] Quick fix --- pokemongo_bot/cell_workers/spin_fort.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pokemongo_bot/cell_workers/spin_fort.py b/pokemongo_bot/cell_workers/spin_fort.py index 5712dd27e0..1f7d971403 100644 --- a/pokemongo_bot/cell_workers/spin_fort.py +++ b/pokemongo_bot/cell_workers/spin_fort.py @@ -284,7 +284,7 @@ def work(self): def get_forts_in_range(self): forts = self.bot.get_forts(order_by_distance=True) forts = filter(lambda fort: fort["id"] not in self.bot.fort_timeouts, forts) - if self.bot.camping_forts and self.try_to_keep_streak: + if hasattr(self.bot, "camping_forts") and self.bot.camping_forts and self.try_to_keep_streak: if datetime.now() >= self.next_update: self.logger.info("Camping forts, ignoring 10 stops streak.") elif self.try_to_keep_streak: