diff --git a/configs/config.json.example b/configs/config.json.example index b2db9aa2f9..2899f578e0 100644 --- a/configs/config.json.example +++ b/configs/config.json.example @@ -183,6 +183,11 @@ "walk_min": 2.16, "alt_min": 500, "alt_max": 1000, + "gps_default_altitude": 8.0, + "replicate_gps_xy_noise": false, + "replicate_gps_z_noise": false, + "gps_xy_noise_range": 0.00025, + "gps_z_noise_range": 12.5, "debug": false, "test": false, "health_record": true, diff --git a/pokecli.py b/pokecli.py index f2fb345c4c..6dfc5042f4 100644 --- a/pokecli.py +++ b/pokecli.py @@ -507,6 +507,48 @@ def _json_loader(filename): type=float, default=1000 ) + add_config( + parser, + load, + long_flag="--replicate_gps_xy_noise", + help="Add noise to current position", + type=bool, + default=False + ) + add_config( + parser, + load, + long_flag="--replicate_gps_z_noise", + help="Add noise to current position", + type=bool, + default=False + ) + add_config( + parser, + load, + long_flag="--gps_xy_noise_range", + help="Intensity of gps noise (unit is lat and lng,) high values may cause issues (default=0.00025)", + type=float, + default=0.00025 + ) + add_config( + parser, + load, + long_flag="--gps_z_noise_range", + help="Intensity of gps noise (unit is in meter, default=12.5)", + type=float, + default=12.5 + ) + add_config( + parser, + load, + long_flag="--gps_default_altitude", + help="Initial altitude (default=8.0)", + type=float, + default=8.0 + ) + + # Start to parse other attrs config = parser.parse_args() if not config.username and 'username' not in load: diff --git a/pokemongo_bot/__init__.py b/pokemongo_bot/__init__.py index a00a78ba3b..7a6f4bc9c5 100644 --- a/pokemongo_bot/__init__.py +++ b/pokemongo_bot/__init__.py @@ -40,11 +40,11 @@ class PokemonGoBot(Datastore): @property def position(self): - return self.api._position_lat, self.api._position_lng, self.api._position_alt + return self.api.actual_lat, self.api.actual_lng, self.api.actual_alt - @position.setter - def position(self, position_tuple): - self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple + #@position.setter # these should be called through api now that gps replication is there... + #def position(self, position_tuple): + # self.api._position_lat, self.api._position_lng, self.api._position_alt = position_tuple @property def player_data(self): @@ -78,7 +78,7 @@ def __init__(self, config): self.last_map_object = None self.last_time_map_object = 0 self.logger = logging.getLogger(type(self).__name__) - self.alt = 1 + self.alt = self.config.gps_default_altitude # Make our own copy of the workers for this instance self.workers = [] @@ -685,9 +685,8 @@ def check_session(self, position): level='info', formatted='Session stale, re-logging in.' ) - position = self.position self.api = ApiWrapper(config=self.config) - self.position = position + self.api.set_position(*self.position) self.login() self.api.activate_signature(self.get_encryption_lib()) @@ -707,7 +706,7 @@ def login(self): formatted="Login procedure started." ) lat, lng = self.position[0:2] - self.api.set_position(lat, lng, 0) + self.api.set_position(lat, lng, self.alt) # or should the alt kept to zero? while not self.api.login( self.config.auth_service, @@ -1045,7 +1044,7 @@ def get_pos_by_name(self, location_name): '[x] Coordinates found in passed in location, ' 'not geocoding.' ) - return float(possible_coordinates[0]), float(possible_coordinates[1]), float("0.0") + return float(possible_coordinates[0]), float(possible_coordinates[1]), self.alt geolocator = GoogleV3(api_key=self.config.gmapkey) loc = geolocator.geocode(location_name, timeout=10) diff --git a/pokemongo_bot/api_wrapper.py b/pokemongo_bot/api_wrapper.py index 8f6c382149..78a4321253 100644 --- a/pokemongo_bot/api_wrapper.py +++ b/pokemongo_bot/api_wrapper.py @@ -13,7 +13,7 @@ from pgoapi.protos.POGOProtos.Networking.Envelopes.Signature_pb2 import Signature from pgoapi.utilities import get_time from pokemongo_bot.datastore import Datastore -from human_behaviour import sleep +from human_behaviour import sleep, gps_noise_rng from pokemongo_bot.base_dir import _base_dir class PermaBannedException(Exception): @@ -25,6 +25,9 @@ class ApiWrapper(Datastore, PGoApi): def __init__(self, config=None): PGoApi.__init__(self) + # Set to default, just for CI... + self.actual_lat, self.actual_lng, self.actual_alt = PGoApi.get_position(self) + self.useVanillaRequest = False self.config = config @@ -77,6 +80,27 @@ def login(self, *args): self.useVanillaRequest = False return ret_value + def set_position(self, lat, lng, alt=None): + self.actual_lat = lat + self.actual_lng = lng + if None != alt: + self.actual_alt = alt + else: + alt = self.actual_alt + + if self.config.replicate_gps_xy_noise: + lat_noise = gps_noise_rng(self.config.gps_xy_noise_range) + lng_noise = gps_noise_rng(self.config.gps_xy_noise_range) + lat = lat + lat_noise + lng = lng + lng_noise + if self.config.replicate_gps_z_noise: + alt_noise = gps_noise_rng(self.config.gps_z_noise_range) + alt = alt + alt_noise + PGoApi.set_position(self, lat, lng, alt) + + def get_position(self): + return (self.actual_lat, self.actual_lng, self.actual_alt) + class ApiRequest(PGoApiRequest): def __init__(self, *args): diff --git a/pokemongo_bot/human_behaviour.py b/pokemongo_bot/human_behaviour.py index 37a95081ca..2c507c805b 100644 --- a/pokemongo_bot/human_behaviour.py +++ b/pokemongo_bot/human_behaviour.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import time -from random import random, uniform +from random import random, uniform, gauss def sleep(seconds, delta=0.3): @@ -25,3 +25,11 @@ def random_lat_long_delta(): # should be 364,000 * .000025 = 9.1. So it returns between [-9.1, 9.1] return ((random() * 0.00001) - 0.000005) * 5 +def gps_noise_rng(radius): + ''' + Simulates gps noise. + ''' + noise = gauss(0, radius/3.0) + noise = min(max(-radius, noise), radius) + return noise + diff --git a/setup.sh b/setup.sh old mode 100755 new mode 100644 diff --git a/tests/__init__.py b/tests/__init__.py index c02aed60fc..e5f7be11fd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -5,7 +5,24 @@ from pokemongo_bot.api_wrapper import ApiWrapper, ApiRequest from pokemongo_bot import PokemonGoBot +import json + +def get_fake_conf(): + class ConfObj: + pass + + conf_dict = json.load(open('configs/config.json.example')) + conf_obj = ConfObj() + for key, value in conf_dict.items(): + setattr(conf_obj, key, value) + + return conf_obj + + class FakeApi(ApiWrapper): + def __init__(self): + super(FakeApi, self).__init__(get_fake_conf()) + def create_request(self, return_value='mock return'): request = ApiWrapper.create_request(self) request.can_call = MagicMock(return_value=True) diff --git a/tests/api_wrapper_test.py b/tests/api_wrapper_test.py index 335f04cbf2..256919a9ac 100644 --- a/tests/api_wrapper_test.py +++ b/tests/api_wrapper_test.py @@ -2,7 +2,7 @@ from mock import MagicMock, patch from timeout_decorator import timeout, TimeoutError -from tests import FakeApi +from tests import FakeApi, get_fake_conf from pgoapi import PGoApi from pgoapi.exceptions import NotLoggedInException, ServerBusyOrOfflineException, NoPlayerPositionSetException, EmptySubrequestChainException @@ -10,7 +10,7 @@ class TestApiWrapper(unittest.TestCase): def test_raises_not_logged_in_exception(self): - api = ApiWrapper() + api = ApiWrapper(get_fake_conf()) api.set_position(*(42, 42, 0)) request = api.create_request() request.get_inventory(test='awesome') @@ -18,17 +18,17 @@ def test_raises_not_logged_in_exception(self): request.call() def test_api_call_with_no_requests_set(self): - request = ApiWrapper().create_request() + request = ApiWrapper(get_fake_conf()).create_request() with self.assertRaises(EmptySubrequestChainException): request.call() def test_api_wrong_request(self): - request = ApiWrapper().create_request() + request = ApiWrapper(get_fake_conf()).create_request() with self.assertRaises(AttributeError): request.wrong_request() def test_raises_no_player_position_set_exception(self): - request = ApiWrapper().create_request() + request = ApiWrapper(get_fake_conf()).create_request() request.get_inventory(test='awesome') with self.assertRaises(NoPlayerPositionSetException): request.call()