def function(**kwargs): if not self._req_method_list: self.log.info('Create 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
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)
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: pass # Trying again will call _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 if 'responses' in response and not response['responses']: self.log.info( "Received empty map_object response. Logging out and retrying." ) auth_provider._ticket_expire = time.time( ) # this will trigger a login in _login_if_necessary else: again = False 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 auth_provider._ticket_expire: remaining_time = auth_provider._ticket_expire / 1000 - time.time() if remaining_time < 60: self.log.info("Login for {} has or is about to expire".format( auth_provider.username)) self._login(auth_provider, position) else: self._login(auth_provider, position)