def function(**kwargs): name = func.upper() position = kwargs.pop('position') callback = kwargs.pop('callback') if kwargs: method = {RequestType.Value(name): kwargs} self.log.debug( "Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: method = RequestType.Value(name) self.log.debug("Adding '%s' to RPC request", name) self.call_method(method, position, callback)
def __getattr__(self, func): def function(**kwargs): if '_call_direct' in kwargs: del kwargs['_call_direct'] self.log.info('Creating a new direct request...') elif not self._req_method_list: self.log.info('Creating a new request...') name = func.upper() if kwargs: self._req_method_list.append({RequestType.Value(name): kwargs}) self.log.info("Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: self._req_method_list.append(RequestType.Value(name)) self.log.info("Adding '%s' to RPC request", name) return self if func.upper() in RequestType.keys(): return function else: raise AttributeError
def _build_sub_requests(self, mainrequest, subrequest_list): self.log.debug('Generating sub RPC requests...') for entry in subrequest_list: if isinstance(entry, dict): entry_id = list(entry.items())[0][0] entry_content = entry[entry_id] entry_name = RequestType.Name(entry_id) proto_name = to_camel_case(entry_name.lower()) + 'Message' proto_classname = 'POGOProtos.Networking.Requests.Messages.' + proto_name + '_pb2.' + proto_name subrequest_extension = self.get_class(proto_classname)() self.log.debug("Subrequest class: %s", proto_classname) for (key, value) in entry_content.items(): if isinstance(value, list): self.log.debug("Found list: %s - trying as repeated", key) for i in value: try: self.log.debug("%s -> %s", key, i) r = getattr(subrequest_extension, key) r.append(i) except Exception as e: self.log.warning('Argument %s with value %s unknown inside %s (Exception: %s)', key, i, proto_name, e) elif isinstance(value, dict): for k in value.keys(): try: r = getattr(subrequest_extension, key) setattr(r, k, value[k]) except Exception as e: self.log.warning('Argument %s with value %s unknown inside %s (Exception: %s)', key, str(value), proto_name, e) else: try: setattr(subrequest_extension, key, value) except Exception as e: try: self.log.debug("%s -> %s", key, value) r = getattr(subrequest_extension, key) r.append(value) except Exception as e: self.log.warning('Argument %s with value %s unknown inside %s (Exception: %s)', key, value, proto_name, e) subrequest = mainrequest.requests.add() subrequest.request_type = entry_id subrequest.request_message = subrequest_extension.SerializeToString() elif isinstance(entry, int): subrequest = mainrequest.requests.add() subrequest.request_type = entry else: raise Exception('Unknown value in request list') return mainrequest
def _parse_sub_responses(self, response_proto, subrequests_list, response_proto_dict): self.log.debug('Parsing sub RPC responses...') response_proto_dict['responses'] = {} if response_proto_dict.get('status_code', 1) == 53: exception = ServerApiEndpointRedirectException() exception.set_redirected_endpoint(response_proto_dict['api_url']) raise exception if 'returns' in response_proto_dict: del response_proto_dict['returns'] list_len = len(subrequests_list) - 1 i = 0 for subresponse in response_proto.returns: if i > list_len: self.log.info("Error - something strange happend...") request_entry = subrequests_list[i] if isinstance(request_entry, int): entry_id = request_entry else: entry_id = list(request_entry.items())[0][0] entry_name = RequestType.Name(entry_id) proto_name = to_camel_case(entry_name.lower()) + 'Response' proto_classname = 'POGOProtos.Networking.Responses.' + proto_name + '_pb2.' + proto_name self.log.debug("Parsing class: %s", proto_classname) subresponse_return = None try: subresponse_extension = self.get_class(proto_classname)() except Exception as e: subresponse_extension = None error = 'Protobuf definition for {} not found'.format( proto_classname) subresponse_return = error self.log.debug(error) if subresponse_extension: try: subresponse_extension.ParseFromString(subresponse) subresponse_return = protobuf_to_dict( subresponse_extension) except: error = "Protobuf definition for {} seems not to match".format( proto_classname) subresponse_return = error self.log.debug(error) response_proto_dict['responses'][entry_name] = subresponse_return i += 1 return response_proto_dict
def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs ) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError
def __getattr__(self, func): def function(**kwargs): request = self.create_request() getattr(request, func)(_call_direct=True, **kwargs) return request.call() if func.upper() in RequestType.keys(): return function else: raise AttributeError
def __getattr__(self, func): def function(**kwargs): name = func.upper() position = kwargs.pop('position') callback = kwargs.pop('callback') if kwargs: method = {RequestType.Value(name): kwargs} self.log.debug( "Adding '%s' to RPC request including arguments", name) self.log.debug("Arguments of '%s': \n\r%s", name, kwargs) else: method = RequestType.Value(name) self.log.debug("Adding '%s' to RPC request", name) self.call_method(method, position, callback) if func.upper() in RequestType.keys(): return function else: raise AttributeError
def list_curr_methods(self): for i in self._req_method_list: print("{} ({})".format(RequestType.Name(i), i))
class PGoApiWorker(Thread): THROTTLE_TIME = 10.0 # In case the server returns a status code 3, this has to be requested SC_3_REQUESTS = [RequestType.Value("GET_PLAYER")] def __init__(self, signature_lib_path, work_queue, auth_queue): Thread.__init__(self) self.log = logging.getLogger(__name__) self._running = True self._work_queue = work_queue self._auth_queue = auth_queue self.rpc_api = RpcApi(None) self.rpc_api.activate_signature(signature_lib_path) def _get_auth_provider(self): while True: # Maybe change this loop to something more beautiful? next_call, auth_provider = self._auth_queue.get() if (time.time() + self.THROTTLE_TIME < next_call): # Probably one of the sidelined auth providers, skip it self._auth_queue.put((next_call, auth_provider)) else: # Sleep until the auth provider is ready if (time.time() < next_call): # Kind of a side effect -> bad time.sleep(max(next_call - time.time(), 0)) return (next_call, auth_provider) def run(self): while self._running: method, position, callback = self._work_queue.get() if not self._running: self._work_queue.put((method, position, callback)) self._work_queue.task_done() continue next_call, auth_provider = self._get_auth_provider() if not self._running: self._auth_queue.put((next_call, auth_provider)) self._work_queue.put((method, position, callback)) self._work_queue.task_done() continue # Let's do this. self.rpc_api._auth_provider = auth_provider try: response = self.call(auth_provider, [method], position) next_call = time.time() + self.THROTTLE_TIME except Exception as e: # Too many login retries lead to an AuthException # So let us sideline this auth provider for 5 minutes if isinstance(e, AuthException): self.log.error( "AuthException in worker thread. Username: {}".format( auth_provider.username)) next_call = time.time() + 5 * 60 else: self.log.error( "Error in worker thread. Returning empty response. Error: {}" .format(e)) next_call = time.time() + self.THROTTLE_TIME self._work_queue.put((method, position, callback)) response = {} self._work_queue.task_done() self.rpc_api._auth_provider = None self._auth_queue.put((next_call, auth_provider)) callback(response) def stop(self): self._running = False def call(self, auth_provider, req_method_list, position): if not req_method_list: raise EmptySubrequestChainException() lat, lng, alt = position if (lat is None) or (lng is None) or (alt is None): raise NoPlayerPositionSetException() self.log.debug('Execution of RPC') response = None again = True # Status code 53 or not logged in? retries = 5 while again: self._login_if_necessary(auth_provider, position) try: response = self.rpc_api.request( auth_provider.get_api_endpoint(), req_method_list, position) if not response: raise ValueError( 'Request returned problematic response: {}'.format( response)) except (NotLoggedInException, AuthTokenExpiredException): pass # Trying again will trigger login in _login_if_necessary except ServerApiEndpointRedirectException as e: auth_provider.set_api_endpoint('https://{}/rpc'.format( e.get_redirected_endpoint())) except Exception as e: # Never crash the worker if isinstance(e, ServerBusyOrOfflineException): self.log.info('Server seems to be busy or offline!') else: self.log.info( 'Unexpected error during request: {}'.format(e)) if retries == 0: return {} retries -= 1 else: if 'api_url' in response: auth_provider.set_api_endpoint('https://{}/rpc'.format( response['api_url'])) if 'status_code' in response and response['status_code'] == 3: self.log.info( "Status code 3 returned. Performing get_player request." ) req_method_list = self.SC_3_REQUESTS + req_method_list auth_provider.code_three_counter += 1 elif 'responses' in response and not response['responses']: self.log.info( "Received empty map_object response. Logging out and retrying." ) auth_provider._access_token_expiry = time.time( ) # This will trigger a login in _login_if_necessary auth_provider.code_three_counter = 0 else: again = False auth_provider.code_three_counter = 0 if auth_provider.code_three_counter > 1: self.log.info( "Received two consecutive status_code 3 on account {}, probably banned." .format(auth_provider.username)) return response def _login(self, auth_provider, position): self.log.info('Attempting login: {}'.format(auth_provider.username)) consecutive_fails = 0 while not auth_provider.user_login(): sleep_t = min(math.exp(consecutive_fails / 1.7), 5 * 60) self.log.info( 'Login failed, retrying in {:.2f} seconds'.format(sleep_t)) consecutive_fails += 1 time.sleep(sleep_t) if consecutive_fails == 5: raise AuthException('Login failed five times.') self.log.info('Login successful: {}'.format(auth_provider.username)) def _login_if_necessary(self, auth_provider, position): if not auth_provider.is_login( ) or auth_provider._access_token_expiry < time.time() + 120: if auth_provider.is_login(): self.log.info( "{} access token has or is about to expire".format( auth_provider.username)) self._login(auth_provider, position)
def _build_main_request(self, subrequests, player_position=None): self.log.debug('Generating main RPC request...') request = RequestEnvelope() request.status_code = 2 request.request_id = self.get_rpc_id() request.accuracy = random.choice((5, 5, 5, 10, 10, 30, 50, 65)) if player_position: request.latitude, request.longitude, altitude = player_position # generate sub requests before SignalAgglomUpdates generation request = self._build_sub_requests(request, subrequests) ticket = self._auth_provider.get_ticket() if ticket: self.log.debug( 'Found Session Ticket - using this instead of oauth token') request.auth_ticket.expire_timestamp_ms, request.auth_ticket.start, request.auth_ticket.end = ticket ticket_serialized = request.auth_ticket.SerializeToString() else: self.log.debug( 'No Session Ticket found - using OAUTH Access Token') request.auth_info.provider = self._auth_provider.get_name() request.auth_info.token.contents = self._auth_provider.get_access_token( ) request.auth_info.token.unknown2 = 59 ticket_serialized = request.auth_info.SerializeToString( ) #Sig uses this when no auth_ticket available if self._signal_agglom_gen: sig = SignalAgglomUpdates() sig.field22 = self.session_hash sig.epoch_timestamp_ms = get_time(ms=True) sig.timestamp_ms_since_start = get_time( ms=True) - RpcApi.START_TIME if sig.timestamp_ms_since_start < 5000: sig.timestamp_ms_since_start = random.randint(5000, 8000) self._hash_engine.hash(sig.epoch_timestamp_ms, request.latitude, request.longitude, request.accuracy, ticket_serialized, sig.field22, request.requests) sig.location_hash_by_token_seed = self._hash_engine.get_location_auth_hash( ) sig.location_hash = self._hash_engine.get_location_hash() for req_hash in self._hash_engine.get_request_hashes(): sig.request_hashes.append(req_hash) loc = sig.location_updates.add() sen = sig.sensor_updates.add() sen.timestamp = random.randint(sig.timestamp_ms_since_start - 5000, sig.timestamp_ms_since_start - 100) loc.timestamp_ms = random.randint( sig.timestamp_ms_since_start - 30000, sig.timestamp_ms_since_start - 1000) loc.name = random.choice( ('network', 'network', 'network', 'network', 'fused')) loc.latitude = request.latitude loc.longitude = request.longitude if not altitude: loc.altitude = random.triangular(300, 400, 350) else: loc.altitude = altitude if random.random() > .95: # no reading for roughly 1 in 20 updates loc.device_course = -1 loc.device_speed = -1 else: self.course = random.triangular(0, 360, self.course) loc.device_course = self.course loc.device_speed = random.triangular(0.2, 4.25, 1) loc.provider_status = 3 loc.location_type = 1 if request.accuracy >= 65: loc.vertical_accuracy = random.triangular(35, 100, 65) loc.horizontal_accuracy = random.choice( (request.accuracy, 65, 65, random.uniform(66, 80), 200)) else: if request.accuracy > 10: loc.vertical_accuracy = random.choice( (24, 32, 48, 48, 64, 64, 96, 128)) else: loc.vertical_accuracy = random.choice( (3, 4, 6, 6, 8, 12, 24)) loc.horizontal_accuracy = request.accuracy sen.acceleration_x = random.triangular(-3, 1, 0) sen.acceleration_y = random.triangular(-2, 3, 0) sen.acceleration_z = random.triangular(-4, 2, 0) sen.magnetic_field_x = random.triangular(-50, 50, 0) sen.magnetic_field_y = random.triangular(-60, 50, -5) sen.magnetic_field_z = random.triangular(-60, 40, -30) sen.magnetic_field_accuracy = random.choice((-1, 1, 1, 2, 2, 2, 2)) sen.attitude_pitch = random.triangular(-1.5, 1.5, 0.2) sen.attitude_yaw = random.uniform(-3, 3) sen.attitude_roll = random.triangular(-2.8, 2.5, 0.25) sen.rotation_rate_x = random.triangular(-6, 4, 0) sen.rotation_rate_y = random.triangular(-5.5, 5, 0) sen.rotation_rate_z = random.triangular(-5, 3, 0) sen.gravity_x = random.triangular(-1, 1, 0.15) sen.gravity_y = random.triangular(-1, 1, -.2) sen.gravity_z = random.triangular(-1, .7, -0.8) sen.status = 3 sig.field25 = 9156899491064153954 if self.device_info: for key in self.device_info: setattr(sig.device_info, key, self.device_info[key]) signal_agglom_proto = sig.SerializeToString() try: if request.requests[0].request_type in ( RequestType.Value('GET_MAP_OBJECTS'), RequestType.Value('GET_PLAYER')): plat = request.platform_requests.add() plat_eight = PlatEight() plat_eight.field1 = 'e40c3e64817d9c96d99d28f6488a2efc40b11046' plat.type = 8 plat.request_message = plat_eight.SerializeToString() except (IndexError, AttributeError): pass sig_request = SendEncryptedSignatureRequest() sig_request.encrypted_signature = self._generate_signature( signal_agglom_proto, sig.timestamp_ms_since_start) plat = request.platform_requests.add() plat.type = 6 plat.request_message = sig_request.SerializeToString() request.ms_since_last_locationfix = 989 self.log.debug('Generated protobuf request: \n\r%s', request) return request