From 330988e1e34562feda6558ce5c9ed03db9330776 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 14 Mar 2017 09:24:28 +0100 Subject: [PATCH 1/2] Sniper fixes Fixes an issue with a source hiding latitude and longitude of Pokemons when using it without a subscription. Also converts IV to float instead of an integer. Some sites use IVs like 96.7 --- pokemongo_bot/cell_workers/sniper.py | 98 +++++++++++++++++----------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/pokemongo_bot/cell_workers/sniper.py b/pokemongo_bot/cell_workers/sniper.py index 6c96fc25ef..db0138efab 100644 --- a/pokemongo_bot/cell_workers/sniper.py +++ b/pokemongo_bot/cell_workers/sniper.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import time +from datetime import datetime, timedelta import json import requests import calendar @@ -9,7 +10,6 @@ from random import uniform from operator import itemgetter, methodcaller -from datetime import datetime from itertools import izip from pokemongo_bot import inventory from pokemongo_bot.item_list import Item @@ -35,19 +35,20 @@ def __str__(self): def fetch_raw(self): some_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/52.0.2743.116 Safari/537.36' response = requests.get(self.url, headers={'User-Agent': some_agent}, timeout=self.timeout) + results = response.json() # If the results is a dict, retrieve the list from it by the given key. This will return a list afterall. - if isinstance(results, dict): - results = results.get(self.key, []) - + if isinstance(results, dict): + results = results.get(self.key, []) + # If results is STILL a dict (eg. each pokemon is its own dict), need to build data from nested json (example whereispokemon.net) while isinstance(results,dict): tmpResults = [] - for key, value in results.iteritems(): + for key, value in results.iteritems(): tmpResults.append(value) results = tmpResults - + return results def fetch(self): @@ -73,6 +74,14 @@ def fetch(self): latitude = position[0] longitude = position[1] + # Some sources block access to all pokemon, need to skip those! + try: + float(latitude) + float(longitude) + except ValueError: + # Seems to be blacked out, do next. + continue + # Format the time accordingly. Pokemon times are in milliseconds! if self.mappings.expiration.exists and expiration: if self.mappings.expiration.format == SniperSourceMappingTimeFormat.SECONDS: @@ -97,7 +106,7 @@ def fetch(self): # Some type castings were specified for a better readability pokemons.append({ - 'iv': int(iv or 0), + 'iv': float(iv or 0), 'pokemon_id': int(id or 0), 'pokemon_name': str(name or ''), 'latitude': float(latitude or .0), @@ -121,7 +130,7 @@ def validate(self): if self.enabled: errors = [] data = self.fetch_raw() - + # Check whether the params really exist if they have been specified like so if data: if self.mappings.iv.exists and self.mappings.iv.param not in data[0]: @@ -154,7 +163,7 @@ def validate(self): raise ValueError("Source not available") except: raise - + def _fixname(self,name): if name: name = name.replace("mr-mime","mr. mime") @@ -166,14 +175,14 @@ def _fixname(self,name): def _get_closest_name(self, name): if not name: return - + pokemon_names = [p.name for p in inventory.pokemons().STATIC_DATA] closest_names = difflib.get_close_matches(name, pokemon_names, 1) if closest_names: closest_name = closest_names[0] return closest_name - + return name # Represents the JSON params mappings @@ -250,10 +259,11 @@ def initialize(self): self.catch_list = self.config.get('catch', {}) self.altitude = uniform(self.bot.config.alt_min, self.bot.config.alt_max) self.sources = [SniperSource(data) for data in self.config.get('sources', [])] + self.no_snipe_until = None if not hasattr(self.bot,"sniper_cache"): self.bot.sniper_cache = [] - + # Dont bother validating config if task is not even enabled if self.enabled: # Validate ordering @@ -283,7 +293,7 @@ def initialize(self): if not self.sources : self._error("There is no source available. Disabling Sniper...") self.disabled = True - + # Re-enable snipping if source is from telegram if self.mode == SniperMode.TELEGRAM: self.disabled = False @@ -310,8 +320,8 @@ def is_snipeable(self, pokemon): self._trace('{} is catchable!'.format(pokemon.get('pokemon_name'))) else: # Not catchable. Having a good IV should suppress the not in catch/vip list (most important) - if pokemon.get('iv', 0) and pokemon.get('iv', 0) <= self.special_iv: - self._trace('{} is not catchable, but has a decent IV!'.format(pokemon.get('pokemon_name'))) + if pokemon.get('iv', 0) and pokemon.get('iv', 0) >= self.special_iv: + self._trace('{} is not catchable, but has a decent IV ({})!'.format(pokemon.get('pokemon_name'), pokemon.get('iv', 0))) else: # Not catchable and IV is not good enough (if any). Check VIP list if pokemon.get('vip', False): @@ -336,7 +346,7 @@ def snipe(self, pokemon): # Have we already tried this pokemon? if not hasattr(self.bot,'sniper_unique_pokemon'): self.bot.sniper_unique_pokemon = [] - + # Check if already in list of pokemon we've tried uniqueid = self._build_unique_id(pokemon) if self._is_cached(uniqueid): @@ -349,13 +359,13 @@ def snipe(self, pokemon): # Teleport, so that we can see nearby stuff self.bot.hb_locked = True self._teleport_to(pokemon) - + # If social is enabled and if no verification is needed, trust it. Otherwise, update IDs! verify = not pokemon.get('encounter_id') or not pokemon.get('spawn_point_id') exists = not verify or self.mode == SniperMode.SOCIAL success = exists - + # Always verify if it's from telegram if TelegramSnipe.ENABLED == True: verify = True @@ -366,12 +376,12 @@ def snipe(self, pokemon): # Wait a maximum of MIN_SECONDS_ALLOWED_FOR_CELL_CHECK seconds before requesting nearby cells self._trace('Pausing for {} secs before checking for Pokemons'.format(self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK)) - + #recode it to check every 5 secs, first check for wild then catchable nearby_pokemons = [] nearby_stuff = [] num = 0 - for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK): + for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK): if num%5 == 0: nearby_stuff = self.bot.get_meta_cell() self.last_cell_check_time = time.time() @@ -380,12 +390,12 @@ def snipe(self, pokemon): nearby_pokemons.extend(nearby_stuff.get('wild_pokemons', [])) if nearby_pokemons: break - + time.sleep(1) num += 1 - + num = 0 - for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK): + for num in range(0,self.MIN_SECONDS_ALLOWED_FOR_CELL_CHECK): if num%5 == 0: nearby_stuff = self.bot.get_meta_cell() self.last_cell_check_time = time.time() @@ -394,16 +404,16 @@ def snipe(self, pokemon): nearby_pokemons.extend(nearby_stuff.get('catchable_pokemons', [])) if nearby_pokemons: break - + time.sleep(1) num += 1 self._trace('Pokemon Nearby: {}'.format(nearby_pokemons)) - + # Make sure the target really/still exists (nearby_pokemon key names are game-bound!) for nearby_pokemon in nearby_pokemons: nearby_pokemon_id = nearby_pokemon.get('pokemon_data', {}).get('pokemon_id') or nearby_pokemon.get('pokemon_id') - + # If we found the target, it exists and will very likely be encountered/caught (success) if nearby_pokemon_id == pokemon.get('pokemon_id', 0): exists = True @@ -419,14 +429,14 @@ def snipe(self, pokemon): if exists: self._log('Yay! There really is a wild {} nearby!'.format(pokemon.get('pokemon_name'))) self._teleport_back_and_catch(last_position, pokemon) - + else: self._error('Damn! Its not here. Reasons: too far, caught, expired or fake data. Skipping...') self._teleport_back(last_position) - + #Set always to false to re-enable sniper to check for telegram data TelegramSnipe.ENABLED = False - + # Save target and unlock heartbeat calls self._cache(uniqueid) self.bot.hb_locked = False @@ -435,17 +445,25 @@ def snipe(self, pokemon): def work(self): #Check if telegram is called - + + if self.no_snipe_until != None and self.no_snipe_until > time.time(): + # No hunting now, cooling down + return WorkerResult.SUCCESS + else: + # Resume hunting + self.no_hunt_until = None + # Do nothing if this task was invalidated if self.disabled: self._error("Sniper was disabled for some reason. Scroll up to find out.") - + elif self.bot.catch_disabled: if not hasattr(self.bot,"sniper_disabled_global_warning") or \ (hasattr(self.bot,"sniper_disabled_global_warning") and not self.bot.sniper_disabled_global_warning): self._log("All catching tasks are currently disabled until {}. Sniper will resume when catching tasks are re-enabled".format(self.bot.catch_resume_at.strftime("%H:%M:%S"))) self.bot.sniper_disabled_global_warning = True - + return WorkerResult.SUCCESS + else: self.bot.sniper_disabled_global_warning = False targets = [] @@ -487,10 +505,14 @@ def work(self): if shots < self.bullets and index < len(targets): self._trace('Waiting a few seconds to teleport again to another target...') time.sleep(3) - + # Always set telegram back to false TelegramSnipe.ENABLED = False + wait = uniform(60, 360) + self.no_snipe_until = time.time() + wait + self._log("Snipe on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) + return WorkerResult.SUCCESS def _parse_pokemons(self, pokemon_dictionary_list): @@ -507,19 +529,19 @@ def _parse_pokemons(self, pokemon_dictionary_list): # Check whether this is a valid target if self.is_snipeable(pokemon): result.append(pokemon) - + return result - + def _get_pokemons_from_telegram(self): if not TelegramSnipe.ENABLED: return {} - + pokemons = [] pokemon = {'iv': int(0), 'pokemon_id': int(TelegramSnipe.ID), 'pokemon_name': str(TelegramSnipe.POKEMON_NAME), 'latitude': float(TelegramSnipe.LATITUDE), 'longitude': float(TelegramSnipe.LONGITUDE)} self._log('Telegram snipe request: {}'.format(pokemon.get('pokemon_name'))) - + pokemons = [pokemon] - + return self._parse_pokemons(pokemons) def _get_pokemons_from_social(self): From ed3fc9da45deb119e715bbb87dd5872a3ec79ef5 Mon Sep 17 00:00:00 2001 From: David Westerink Date: Tue, 14 Mar 2017 13:14:55 +0100 Subject: [PATCH 2/2] Added config for cooldown/loiter --- configs/config.json.example | 2 ++ docs/configuration_files.md | 2 ++ pokemongo_bot/cell_workers/sniper.py | 18 ++++++++++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/configs/config.json.example b/configs/config.json.example index 980a56a32d..b0a8547030 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -291,6 +291,8 @@ "mode": "social", "bullets": 1, "homing_shots": true, + "cooldown_enabled": false, + "loiter_after_snipe": false, "special_iv": 100, "order": [ "missing", diff --git a/docs/configuration_files.md b/docs/configuration_files.md index 12deaf8523..eb4700621a 100644 --- a/docs/configuration_files.md +++ b/docs/configuration_files.md @@ -781,6 +781,8 @@ This task is an upgrade version of the MoveToMapPokemon task. It will fetch poke * `homing_shots` - This will ensure that each bullet **will catch** a target. If disabled, a target might not exist and thus it wont be caught. When enabled, this will jump to the next target (if any) and try again to catch it. This will be repeated untill you've spent all the bullets. (default: true) * `special_iv` - This will skip the catch list if the value is greater than or equal to the target's IV. This currently does not work with `social` mode and only works if the given `url` has this information. (default: 100) * `time_mask` - The time mask used (if `expiration.format` is a full date). The default mask is '%Y-%m-%d %H:%M:%S'. +* `cooldown_enabled` - Do we set the sniper on a cool down of a random time after snipping? This might help avoiding bans. +* `loiter_after_snipe` - Do we wait a random time after sniping (aside of the above cooldown)? This might also help avoiding bans. * `order` - The order on which you want to snipe. This can be one or multiple of the following values (default: [`missing`, `vip`, `priority`]): - `iv` - Order by IV, if any. See `special_iv`. - `vip` - Order by VIP. diff --git a/pokemongo_bot/cell_workers/sniper.py b/pokemongo_bot/cell_workers/sniper.py index db0138efab..3c6cf276c1 100644 --- a/pokemongo_bot/cell_workers/sniper.py +++ b/pokemongo_bot/cell_workers/sniper.py @@ -134,7 +134,7 @@ def validate(self): # Check whether the params really exist if they have been specified like so if data: if self.mappings.iv.exists and self.mappings.iv.param not in data[0]: - errors.append(self.mappings.iv.param) + errors.append(self.mappings.iv.param) if self.mappings.id.exists and self.mappings.id.param not in data[0]: errors.append(self.mappings.id.param) if self.mappings.name.exists and self.mappings.name.param not in data[0]: @@ -256,6 +256,8 @@ def initialize(self): self.homing_shots = self.config.get('homing_shots', True) self.mode = self.config.get('mode', SniperMode.DEFAULT) self.order = self.config.get('order', SniperOrderMode.DEFAULT) + self.cooldown_enabled = self.config.get('cooldown_enabled', False) + self.loiter_after_snipe = self.config.get('loiter_after_snipe', False) self.catch_list = self.config.get('catch', {}) self.altitude = uniform(self.bot.config.alt_min, self.bot.config.alt_max) self.sources = [SniperSource(data) for data in self.config.get('sources', [])] @@ -467,6 +469,7 @@ def work(self): else: self.bot.sniper_disabled_global_warning = False targets = [] + sniped = False # Retrieve the targets if self.mode == SniperMode.SOCIAL: @@ -493,6 +496,7 @@ def work(self): # For as long as there are targets available, try to snipe untill we run out of bullets for index, target in enumerate(targets): + sniped = True if shots < self.bullets: success = self.snipe(target) shots += 1 @@ -509,9 +513,15 @@ def work(self): # Always set telegram back to false TelegramSnipe.ENABLED = False - wait = uniform(60, 360) - self.no_snipe_until = time.time() + wait - self._log("Snipe on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) + if sniped: + if self.loiter_after_snipe: + loiter = int(uniform(20, 40)) + self._log("Loitering for {} seconds after sniping to allow Niantic flags to drop off...".format(loiter)) + time.sleep(loiter) + if self.cooldown_enabled: + wait = uniform(60, 360) + self.no_snipe_until = time.time() + wait + self._log("Snipe on cooldown until {}.".format((datetime.now() + timedelta(seconds=wait)).strftime("%H:%M:%S"))) return WorkerResult.SUCCESS