예제 #1
0
파일: pgoapi.py 프로젝트: notpike/pgoapi
        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
예제 #2
0
        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)
예제 #3
0
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)