diff --git a/configs/config.json.example b/configs/config.json.example index 71d098d45a..11cb2c88f1 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -471,7 +471,13 @@ "order_by": "cp", "min_interval":360, "min_recheck":30, - "max_recheck":120 + "max_recheck":120, + "chain_fill_gyms": true, + "ignore_max_cp_pokemon": ["Blissey"], + "never_place": ["Machamp"], + "leave_at_least_spots": 1, + "take_at_most": 10, + "pick_random_pokemon": true } }, { diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 853bfce16d..a32a76c0bb 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -1516,3 +1516,47 @@ If you have any Pokemon that are dead or need healing, this task will try to do } ``` +## GymPokemon +[[back to top](#table-of-contents)] + +### Description +[[back to top](#table-of-contents)] + +Drop a Pokemon in a Gym when there is room for. No fighting will be done! Never! + +### Options +[[back to top](#table-of-contents)] + +* `enabled`: `Default: False`. Enable or distable this task +* `order_by`: `Default: "cp"`. Based on what atrribute should be sorted (decending) +* `min_interval`: `Default: 360`. Time in seconds between printing the Pokemon we have in gyms +* `min_recheck`: `Default: 30`. Minimal time in seconds to check for new gyms in range +* `max_recheck`: `Default: 120`. Maxium time in seconds to check for new gyms in range +* `chain_fill_gyms`: `Default: True`. When we drop a Pokemon in a gym, should we imediatly move to the next gym if a open spot is found? +* `ignore_max_cp_pokemon`: `Default: ["Blissey"]`. A list of Pokemon who can be placed in gyms even if above 3000cp +* `never_place`: `Default: []`. A list of Pokemon that should never be placed in a gym +* `leave_at_least_spots`: `Default: 0`. How many open spots should we leave for normal players? (Max 4!!) +* `take_at_most`: `Default: 20`. How many gym should we be in at the same time? Max 20!! +* `pick_random_pokemon`: `Default: True`. Pick a random Pokemon from the top 20 sorted by "order_by", or the top 1 availible? + + +### Sample configuration +[[back to top](#table-of-contents)] +```json +{ + "type": "GymPokemon", + "config": { + "enabled": false, + "order_by": "cp", + "min_interval":360, + "min_recheck":30, + "max_recheck":120, + "chain_fill_gyms": true, + "ignore_max_cp_pokemon": ["Blissey"], + "never_place": ["Machamp"], + "leave_at_least_spots": 1, + "take_at_most": 10, + "pick_random_pokemon": true + } +} +``` diff --git a/docs/installation.md b/docs/installation.md index e12de2bad1..31bae0c444 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -11,7 +11,7 @@ - [pip](https://pip.pypa.io/en/stable/installing/) - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended) -- [hashing key](http://hashing.pogodev.org) - if you want use latest API, not the old, 0.45 +- [hashing key](http://hashing.pogodev.org) - Hashing key now required for bot operation ### Easy installation 1. Clone the git: `git clone https://github.com/PokemonGoF/PokemonGo-Bot` @@ -27,13 +27,13 @@ This will reset and makes sure you have no changes made to any code since it will overide it 3. Rerun the bot `./run.sh` -for manual installation please refer to [here](https://github.com/PokemonGoF/PokemonGo-Bot/blob/dev/docs/manual_installation.md) +For manual installation please refer to [here](https://github.com/PokemonGoF/PokemonGo-Bot/blob/dev/docs/manual_installation.md) # Windows We do recommend Windows users to use [Docker](#docker) this will work much easier and smoother (also safer) ## Requirements -- [hashing key](http://hashing.pogodev.org) - if you want use latest API, not the old, 0.45 +- [hashing key](http://hashing.pogodev.org) - Hashing key now required for bot operation ### Easy Installation 1. Download [PokemonGo-Bot-Install.bat](https://github.com/PokemonGoF/PokemonGo-Bot/blob/master/windows_bat/PokemonGo-Bot-Install.bat) @@ -51,70 +51,73 @@ This will check for an update and will start the bot afterwards. ### Easy installation Start by downloading for your platform: + - [Mac](https://www.docker.com/products/docker#/mac) - [Windows](https://www.docker.com/products/docker#/windows) - [Linux](https://www.docker.com/products/docker#/linux) -Once you have Docker installed, simply create the various config files for your different accounts (e.g. `configs/config.json`, `configs/userdata.js`) and then create a Docker image for PokemonGo-Bot using the Dockerfile in this repo. + +- Once you have Docker installed, simply create the various config files for your different accounts (e.g. `configs/config.json`, `configs/userdata.js`) and then create a Docker image for PokemonGo-Bot using the Dockerfile in this repo. ``` cd PokemonGo-Bot docker build -t pokemongo-bot . ``` -By default our Dockerfile ensures that the "master" branch will be used for building the docker container, if you want to use the "dev" branch then you should build the container with below build command: +- By default our Dockerfile ensures that the "master" branch will be used for building the docker container, if you want to use the "dev" branch then you should build the container with below build command: ``` docker build --build-arg BUILD_BRANCH=dev -t pokemongo-bot . ``` - - -After build process you can verify that the image was created with: +- After build process you can verify that the image was created with: ``` docker images ``` -To run the bot container with the PokemonGo-Bot Docker image you've created: +- To run the bot container with the PokemonGo-Bot Docker image you've created: ``` docker run --name=bot1-pokego --rm -it -v $(pwd)/configs/config.json:/usr/src/app/configs/config.json pokemongo-bot ``` -Optionally you can set your timezone with the -e option (default is Etc/UTC). You can find an exhaustive list of timezone here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +- Optionally you can set your timezone with the -e option (default is Etc/UTC). You can find an exhaustive list of timezone here: [https://en.wikipedia.org/wiki/List_of_tz_database_time_zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). ``` docker run --name=bot1-pokego --rm -it -e TZ=Asia/Taipei -v $(pwd)/configs/config.json:/usr/src/app/configs/config.json pokemongo-bot ``` ->In the case you configured authentification to be handled by auth.json file make sure you mount that file as a volume also +- In the case you configured authentication to be handled by auth.json file make sure you mount that file as a volume also. ->``` +``` docker run --name=bot1-pokego --rm -it -v $(pwd)/configs/auth.json:/usr/src/app/configs/auth.json -v $(pwd)/configs/config.json:/usr/src/app/configs/config.json -v $(pwd)/web/:/usr/src/app/web/ pokemongo-bot ``` ->or for a simplified version mount your whole configs/ subdir to /usr/src/app/configs +- For a simplified version mount your whole configs/ subdir to /usr/src/app/configs. ->``` +``` docker run --name=bot1-pokego --rm -it -v $(pwd)/configs:/usr/src/app/configs -v $(pwd)/web/:/usr/src/app/web/ pokemongo-bot ``` -> -Run a second container provided with the OpenPoGoBotWeb view: +- Run a second container provided with the OpenPoGoBotWeb view: ``` docker run --name=bot1-pokegoweb --rm -it --volumes-from bot1-pokego -p 8000:8000 -v $(pwd)/configs/userdata.js:/usr/src/app/web/config/userdata.js -w /usr/src/app/web python:2.7 python -m SimpleHTTPServer ``` -The OpenPoGoWeb will be served on `http://:8000` + +- The OpenPoGoWeb will be served on `http://:8000` ### Using proxy with docker: - https proxy - ``` + +``` docker run --name=bot1-pokego -e "https_proxy=https://PROXY_IP:PORT" --rm -it -v $(pwd)/configs:/usr/src/app/configs -v $(pwd)/web/:/usr/src/app/web/ pokemongocc-bot ``` -- http proxy + +* http proxy + ``` docker run --name=bot1-pokego -e "http_proxy=http://PROXY_IP:PORT" --rm -it -v $(pwd)/configs:/usr/src/app/configs -v $(pwd)/web/:/usr/src/app/web/ pokemongo-bot ``` @@ -139,14 +142,14 @@ docker run --name=bot1-pokegoweb --rm -it --volumes-from bot1-pokego -p 8000:800 - Retrieve your host address: ``` - docker-machine ip default +docker-machine ip default ``` -Then, with your containers running and your host address, you can access the web view in your browser: +- Then, with your containers running and your host address, you can access the web view in your browser: + +`http://:8000` (e.g. http://192.168.99.100:8000) -`http://:8000 (eg http://192.168.99.100:8000)` -``` #### Errors - An error occurred trying to connect: @@ -164,28 +167,26 @@ Make sure that the name of the image is correct. ### Using Docker compose -if docker-compose [installed](https://docs.docker.com/compose/install/) you can alternatively run the PokemonGo-Bot ecosystem with one simple command: +- If docker-compose [installed](https://docs.docker.com/compose/install/) you can alternatively run the PokemonGo-Bot ecosystem with one simple command: (by using the docker-compose.yml configuration in this repo) ``` docker-compose up ``` -An example of routing the bot's traffic through a tor proxy can be found within the docker-compose_tor.yml file. To use a different file, supply the file name to docker-compose. The d flag is used to run this in detached mode as the tor logs overwhelm any bot logs you might wish to view. The bot logs can still be seen through `docker logs` command. +- An example of routing the bot's traffic through a tor proxy can be found within the docker-compose_tor.yml file. To use a different file, supply the file name to docker-compose. The d flag is used to run this in detached mode as the tor logs overwhelm any bot logs you might wish to view. The bot logs can still be seen through `docker logs` command. ``` docker-compose -f docker-compose_tor.yml up -d ``` -Also run one single service from the compose configuration is possible: +- Also run one single service from the compose configuration is possible: ``` docker-compose run --rm bot1-pokego ``` - - -command to stop and remove all stopped containers: `docker-compose down` +- command to stop and remove all stopped containers: `docker-compose down` TODO: Add infos / configuration for running multiple bot instances. @@ -193,8 +194,10 @@ Do not push your image to a registry with your config.json and account details i ### Bug reporting when using docker: -Please include output of below command: +* Please include output of below command: + ``` docker inspect --format='{{.Created}} {{.ContainerConfig.Labels}}' container_tag_or_id ``` -container_tag_or_id being the final tag_id of container or the id of the intermediary layer at which the docker build failed. + +`container_tag_or_id` being the final tag_id of container or the id of the intermediary layer at which the docker build failed. \ No newline at end of file diff --git a/pokecli.py b/pokecli.py index e0fb7c4fb5..5efd00ab23 100644 --- a/pokecli.py +++ b/pokecli.py @@ -66,7 +66,7 @@ try: import pkg_resources pgoapi_version = pkg_resources.get_distribution("pgoapi").version - if pgoapi_version != '1.2.0': + if pgoapi_version != '1.2.1': print "Run following command to get latest update: `pip install -r requirements.txt --upgrade`" sys.exit(1) except pkg_resources.DistributionNotFound: diff --git a/pokemongo_bot/cell_workers/complete_tutorial.py b/pokemongo_bot/cell_workers/complete_tutorial.py index d3f875f020..88580ddb8e 100644 --- a/pokemongo_bot/cell_workers/complete_tutorial.py +++ b/pokemongo_bot/cell_workers/complete_tutorial.py @@ -119,13 +119,13 @@ def _random_avatar(self): # at the first choices in general, so fully # random on the whole avatar space is not the way to go either avatar['skin']=random.randint(0,3) - avatar['hair']=random.randint(0,3) - avatar['shirt']=random.randint(0,3) - avatar['pants']=random.randint(0,3) - avatar['hat']=random.randint(0,3) - avatar['shoes']=random.randint(0,3) - avatar['eyes']=random.randint(0,3) - avatar['backpack']=random.randint(0,3) + avatar['avartar_hair']=random.randint(0,3) + avatar['avartar_shirt']=random.randint(0,3) + avatar['avartar_pants']=random.randint(0,3) + avatar['avartar_hat']=random.randint(0,3) + avatar['avartar_shoes']=random.randint(0,3) + avatar['avartar_eyes']=random.randint(0,3) + avatar['avartar_backpack']=random.randint(0,3) return avatar def _set_avatar(self): diff --git a/pokemongo_bot/cell_workers/gym_pokemon.py b/pokemongo_bot/cell_workers/gym_pokemon.py index ec1ec91549..e3bb084e2c 100644 --- a/pokemongo_bot/cell_workers/gym_pokemon.py +++ b/pokemongo_bot/cell_workers/gym_pokemon.py @@ -23,6 +23,8 @@ from pokemongo_bot.walkers.walker_factory import walker_factory from pokemongo_bot.inventory import Pokemons +from sys import stdout + GYM_DETAIL_RESULT_SUCCESS = 1 GYM_DETAIL_RESULT_OUT_OF_RANGE = 2 GYM_DETAIL_RESULT_UNSET = 0 @@ -52,10 +54,6 @@ 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') @@ -63,7 +61,21 @@ 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.take_at_most = self.config.get('take_at_most', 20) + if self.take_at_most > 20: + self.logger.warning("We can take more than 20 gyms!") + self.take_at_most = 20 + self.leave_at_least_spots = self.config.get('leave_at_least_spots', 0) + if self.leave_at_least_spots > 4: + self.logger.warning("There are only 6 spots in a gym, when we drop a Pokemon in that would leave 5 spots! Setting leave open spots to 4!") + self.leave_at_least_spots = 4 + self.chain_fill_gyms = self.config.get('chain_fill_gyms', True) + self.ignore_max_cp_pokemon = self.config.get('allow_above_cp', ["Blissey"]) + self.never_place = self.config.get('never_place', []) + + self.pick_random_pokemon = self.config.get('pick_random_pokemon', True) + self.recheck = datetime.now() self.walker = self.config.get('walker', 'StepWalker') self.destination = None @@ -77,6 +89,7 @@ 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') @@ -125,9 +138,9 @@ def work(self): if self.bot.softban: return WorkerResult.SUCCESS - if len(self.fort_pokemons) >= 20: + if len(self.fort_pokemons) >= self.take_at_most: if self._should_print(): - self.logger.info("We have a max of 20 Pokemon in gyms.") + self.logger.info("We have a max of %s Pokemon in gyms." % self.take_at_most) return WorkerResult.SUCCESS if not self.should_run(): @@ -172,8 +185,6 @@ 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) - if 'gym_display' in gym: display = gym['gym_display'] if 'slots_available' in display: @@ -226,12 +237,17 @@ def determin_new_destination(self): 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: - #If there are mons we can feed, check them. Note: gym_details returns false if gym is not in range. - self.feed_pokemons_in_gym(gym) + if self.leave_at_least_spots > 0: + if display['slots_available'] > self.leave_at_least_spots: + self.logger.info("Gym has %s open spots!" % display['slots_available']) + self.destination = gym + break + else: + self.logger.info("Gym has %s open spots, but we don't drop Pokemon in it because that would leave less than %s open spots" % (display['slots_available'], self.leave_at_least_spots)) + else: + 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) @@ -304,16 +320,20 @@ def move_to_destination(self): 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: + if len(self.fort_pokemons) >= self.take_at_most: + self.logger.info("We have a max of %s Pokemon in gyms." % self.take_at_most) return WorkerResult.SUCCESS + elif self.chain_fill_gyms: + # 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 else: - return WorkerResult.RUNNING + return WorkerResult.SUCCESS def get_gym_details(self, gym): lat = gym['latitude'] @@ -354,164 +374,7 @@ def _get_pokemons_in_gym(self, gym_details): pokemon_names.append(Pokemons.name_for(pokemon_id)) 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 - - #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.") - return True - - max_gym_time = timedelta(hours=8,minutes=20) - - try: - gym_info = self.get_gym_details(gym).get('gym_status_and_defenders', None) - 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("LOG: Defender data: defender %s" % defender) - - motivated_pokemon = defender.get('motivated_pokemon') - pokemon_info = motivated_pokemon.get('pokemon') - 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 - # % 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_id - - #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=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 - 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): - - 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" % 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=self.gym_quantity_test, - 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'] - self.logger.info("Feeding result: %s" % feeding) - result = feeding.get('result', -1) - if result == 1: - # Succesful feeding - self.emit_event( - 'fed_pokemon', - 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] @@ -566,12 +429,22 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): self.raid_gyms[gym["id"]] = raid_ends return WorkerResult.SUCCESS else: + first_time = False 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: + raid_ending = (raid_ends-datetime.today()).seconds + sleep_m, sleep_s = divmod(raid_ending, 60) + sleep_h, sleep_m = divmod(sleep_m, 60) + sleep_hms = '%02d:%02d:%02d' % (sleep_h, sleep_m, sleep_s) + if not first_time: + # Clear the last log line! + stdout.write("\033[1A\033[0K\r") + stdout.flush() + first_time = True + self.logger.info("Waiting for %s for raid to end..." % sleep_hms) + if raid_ending > 20: sleep(20) else: - sleep((raid_ends-datetime.today()).seconds) + sleep(raid_ending) break else: self.logger.info("Raid has not begun yet!") @@ -584,14 +457,23 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): if lockout_time > datetime.now(): self.logger.info("Lockout time: %s" % lockout_time.strftime('%Y-%m-%d %H:%M:%S.%f')) + first_time = False 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 > 40: + lockout_ending = (lockout_time-datetime.today()).seconds + sleep_m, sleep_s = divmod(lockout_ending, 60) + sleep_h, sleep_m = divmod(sleep_m, 60) + sleep_hms = '%02d:%02d:%02d' % (sleep_h, sleep_m, sleep_s) + if not first_time: + # Clear the last log line! + stdout.write("\033[1A\033[0K\r") + stdout.flush() + first_time = True + self.logger.info("Waiting for %s deployment lockout to end..." % sleep_hms) + if lockout_ending > 40: sleep(40) - #Feed any mons while we are waiting - self.feed_pokemons_in_gym(gym) + break else: - sleep((lockout_time-datetime.today()).seconds) + sleep(lockout_ending) break #FortDeployPokemon @@ -621,6 +503,7 @@ def drop_pokemon_in_gym(self, gym, current_pokemons): # self.logger.info("Status: %s" % result) if result == 1: self.dropped_gyms.append(gym["id"]) + self.fort_pokemons.append(fort_pokemon) 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))) @@ -763,9 +646,11 @@ def get_poke_info(info, pokemon): # 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] + possible_pokemons = [p for p in possible_pokemons if p.cp < 3000 and p.name not in self.ignore_max_cp_pokemon] # Filter out "bad" Pokemon possible_pokemons = [p for p in possible_pokemons if not p.is_bad] + # Filter out "never place" Pokemon + possible_pokemons = [p for p in possible_pokemons if p.name not in self.never_place] # 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] @@ -773,6 +658,7 @@ def get_poke_info(info, pokemon): pokemons_ordered = sorted(possible_pokemons, key=lambda x: get_poke_info(self.order_by, x), reverse=True) # Top 20 picks pokemons_ordered = pokemons_ordered[0:20] - # Pick a random one! - random.shuffle(pokemons_ordered) + if self.pick_random_pokemon: + # Pick a random one! + random.shuffle(pokemons_ordered) return pokemons_ordered[0] diff --git a/pokemongo_bot/cell_workers/heal_pokemon.py b/pokemongo_bot/cell_workers/heal_pokemon.py index c5cde80688..dcfab55a49 100644 --- a/pokemongo_bot/cell_workers/heal_pokemon.py +++ b/pokemongo_bot/cell_workers/heal_pokemon.py @@ -22,6 +22,9 @@ def __init__(self, bot, config): self.next_update = None self.to_heal = [] + self.warned_about_no_revives = False + self.warned_about_no_potions = False + def work(self): if not self.enabled: @@ -59,17 +62,23 @@ def work(self): 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)) + if not self.warned_about_no_revives: + self.logger.info("No revives left! Can't revive %s pokemons." % len(to_revive)) + self.warned_about_no_revives = True elif len(to_revive) > 0: self.logger.info("Reviving %s pokemon..." % len(to_revive)) + self.warned_about_no_revives = False 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)) + if not self.warned_about_no_potions: + self.logger.info("No potions left! Can't heal %s pokemon" % len(self.to_heal)) + self.warned_about_no_potions = True elif len(self.to_heal) > 0: self.logger.info("Healing %s pokemon" % len(self.to_heal)) + self.warned_about_no_potions = False for pokemon in self.to_heal: self._heal_pokemon(pokemon) @@ -185,6 +194,10 @@ def hp_to_restore(pokemon): def _use_potion(self, potion_id, pokemon): + if pokemon.hp >= pokemon.hp_max: + # Already at MAX health + return True + potion_count = inventory.items().get(potion_id).count healing = 0 if potion_count == 0: diff --git a/pokemongo_bot/cell_workers/pokemon_catch_worker.py b/pokemongo_bot/cell_workers/pokemon_catch_worker.py index 6ee0ba9482..57db7fa703 100644 --- a/pokemongo_bot/cell_workers/pokemon_catch_worker.py +++ b/pokemongo_bot/cell_workers/pokemon_catch_worker.py @@ -219,6 +219,8 @@ def work(self, response_dict=None): encounter_id = self.pokemon['encounter_id'] catch_rate_by_ball = [0] + response['capture_probability']['capture_probability'] # offset so item ids match indces self._do_catch(pokemon, encounter_id, catch_rate_by_ball, is_vip=is_vip) + #We need to continue all tasks when we are ok + return WorkerResult.SUCCESS break else: self.emit_event('catch_limit', formatted='WARNING! You have reached your daily catch limit') @@ -227,6 +229,8 @@ def work(self, response_dict=None): # simulate app time.sleep(5) + #We need to continue all tasks when we are ok + return WorkerResult.SUCCESS def create_encounter_api_call(self): encounter_id = self.pokemon['encounter_id'] diff --git a/pokemongo_bot/event_manager.py b/pokemongo_bot/event_manager.py index 1902a5790f..42863855ec 100644 --- a/pokemongo_bot/event_manager.py +++ b/pokemongo_bot/event_manager.py @@ -52,7 +52,8 @@ def __init__(self, event, sender=None, level='info', formatted='', data={}): self.sender = str(sender).encode('ascii', 'xmlcharrefreplace') self.level = str(level).encode('ascii', 'xmlcharrefreplace') - self.formatted = str(formatted).encode('ascii', 'xmlcharrefreplace') + #self.formatted = str(formatted).encode('ascii', 'xmlcharrefreplace') + self.formatted = str(formatted).encode('utf-8', 'xmlcharrefreplace') self.data = str(data).encode('ascii', 'xmlcharrefreplace') self.friendly_msg = ""