diff --git a/.gitignore b/.gitignore index bfdbdc60..1dc170f3 100644 --- a/.gitignore +++ b/.gitignore @@ -89,4 +89,8 @@ ENV/ .ropeproject # Personal load details -config.json \ No newline at end of file +config.json + +# Encrpytion libs +encrypt.dll +libencrypt.so \ No newline at end of file diff --git a/config.json.example b/config.json.example index c112f5ce..31be66e2 100644 --- a/config.json.example +++ b/config.json.example @@ -2,5 +2,6 @@ "auth_service": "google", "username": "example@gmail.com", "password": "password11!!", - "location": "New York" + "location": "New York", + "encrypt": "./libencrypt.so" } diff --git a/pgoapi/__init__.py b/pgoapi/__init__.py index d5b807b6..f37772fc 100755 --- a/pgoapi/__init__.py +++ b/pgoapi/__init__.py @@ -31,7 +31,7 @@ import logging __title__ = 'pgoapi' -__version__ = '1.1.6' +__version__ = '1.1.7' __author__ = 'tjado' __license__ = 'MIT License' __copyright__ = 'Copyright (c) 2016 tjado ' diff --git a/pgoapi/auth.py b/pgoapi/auth.py index a43a979b..2b2ccddd 100755 --- a/pgoapi/auth.py +++ b/pgoapi/auth.py @@ -26,7 +26,7 @@ from __future__ import absolute_import import logging -from pgoapi.utilities import get_time_ms, get_format_time_diff +from pgoapi.utilities import get_time, get_format_time_diff class Auth: @@ -36,8 +36,21 @@ def __init__(self): self._auth_provider = None self._login = False + + """ + oauth2 uses refresh tokens (which basically never expires) + to get an access_token which is only valid for a certain time) + """ + self._refresh_token = None + self._access_token = None + self._access_token_expiry = 0 + # TODO: can be removed self._auth_token = None + """ + Pokemon Go uses internal tickets, like an internal + session to keep a user logged in over a certain time (30 minutes) + """ self._ticket_expire = None self._ticket_start = None self._ticket_end = None @@ -49,7 +62,7 @@ def is_login(self): return self._login def get_token(self): - return self._auth_token + return self._access_token def has_ticket(self): if self._ticket_expire and self._ticket_start and self._ticket_end: @@ -68,13 +81,13 @@ def is_new_ticket(self, new_ticket_time_ms): def check_ticket(self): if self.has_ticket(): - now_ms = get_time_ms() + now_ms = get_time(ms = True) if now_ms < (self._ticket_expire - 10000): h, m, s = get_format_time_diff(now_ms, self._ticket_expire, True) - self.log.debug('Auth ticket still valid for further %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, self._ticket_expire) + self.log.debug('Session Ticket still valid for further %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, self._ticket_expire) return True else: - self.log.debug('Removed expired auth ticket (%s < %s)', now_ms, self._ticket_expire) + self.log.debug('Removed expired Session Ticket (%s < %s)', now_ms, self._ticket_expire) self._ticket_expire, self._ticket_start, self._ticket_end = (None, None, None) return False else: @@ -86,5 +99,34 @@ def get_ticket(self): else: return False - def login(self, username, password): - raise NotImplementedError() \ No newline at end of file + def user_login(self, username, password): + raise NotImplementedError() + + def set_refresh_token(self, username, password): + raise NotImplementedError() + + def get_access_token(self, force_refresh = False): + raise NotImplementedError() + + + def check_access_token(self): + """ + Add few seconds to now so the token get refreshed + before it invalidates in the middle of the request + """ + now_s = get_time() + 120 + + if self._access_token is not None: + if self._access_token_expiry == 0: + self.log.debug('No Access Token Expiry found - assuming it is still valid!') + return True + elif self._access_token_expiry > now_s: + h, m, s = get_format_time_diff(now_s, self._access_token_expiry, False) + self.log.debug('Access Token still valid for further %02d:%02d:%02d hours (%s < %s)', h, m, s, now_s, self._access_token_expiry) + return True + else: + self.log.info('Access Token expired!') + return False + else: + self.log.debug('No Access Token available!') + return False \ No newline at end of file diff --git a/pgoapi/auth_google.py b/pgoapi/auth_google.py index 8baa6ef5..8dd75c7a 100755 --- a/pgoapi/auth_google.py +++ b/pgoapi/auth_google.py @@ -25,9 +25,11 @@ from __future__ import absolute_import +import six import logging from pgoapi.auth import Auth +from pgoapi.exceptions import AuthException from gpsoauth import perform_master_login, perform_oauth class AuthGoogle(Auth): @@ -39,24 +41,58 @@ class AuthGoogle(Auth): def __init__(self): Auth.__init__(self) - + self._auth_provider = 'google' - def login(self, username, password): - self.log.info('Google login for: {}'.format(username)) - login = perform_master_login(username, password, self.GOOGLE_LOGIN_ANDROID_ID) - login = perform_oauth(username, login.get('Token', ''), self.GOOGLE_LOGIN_ANDROID_ID, self.GOOGLE_LOGIN_SERVICE, self.GOOGLE_LOGIN_APP, - self.GOOGLE_LOGIN_CLIENT_SIG) - - self._auth_token = login.get('Auth') - - if self._auth_token is None: - self.log.info('Google Login failed.') - return False - - self._login = True - - self.log.info('Google Login successful.') - self.log.debug('Google Session Token: %s', self._auth_token[:25]) - - return True \ No newline at end of file + self._refresh_token = None + + def user_login(self, username, password): + self.log.info('Google User Login for: {}'.format(username)) + + if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): + raise AuthException("Username/password not correctly specified") + + user_login = perform_master_login(username, password, self.GOOGLE_LOGIN_ANDROID_ID) + + refresh_token = user_login.get('Token', None) + if refresh_token is not None: + self._refresh_token = refresh_token + self.log.info('Google User Login successful.') + else: + self._refresh_token = None + raise AuthException("Invalid Google Username/password") + + self.get_access_token() + + def set_refresh_token(self, refresh_token): + self.log.info('Google Refresh Token provided by user') + self._refresh_token = refresh_token + + def get_access_token(self, force_refresh = False): + token_validity = self.check_access_token() + + if token_validity is True and force_refresh is False: + self.log.debug('Using cached Google Access Token') + return self._access_token + else: + if force_refresh: + self.log.info('Forced request of Google Access Token!') + else: + self.log.info('Request Google Access Token...') + + token_data = perform_oauth(None, self._refresh_token, self.GOOGLE_LOGIN_ANDROID_ID, self.GOOGLE_LOGIN_SERVICE, self.GOOGLE_LOGIN_APP, + self.GOOGLE_LOGIN_CLIENT_SIG) + + access_token = token_data.get('Auth', None) + if access_token is not None: + self._access_token = access_token + self._access_token_expiry = int(token_data.get('Expiry', 0)) + self._login = True + + self.log.info('Google Access Token successfully received.') + self.log.debug('Google Access Token: %s...', self._access_token[:25]) + return self._access_token + else: + self._access_token = None + self._login = False + raise AuthException("Could not receive a Google Access Token") diff --git a/pgoapi/auth_ptc.py b/pgoapi/auth_ptc.py index 8190dbeb..3c5e1877 100755 --- a/pgoapi/auth_ptc.py +++ b/pgoapi/auth_ptc.py @@ -24,13 +24,20 @@ """ from __future__ import absolute_import +from future.standard_library import install_aliases +install_aliases() import re +import six import json import logging import requests +from urllib.parse import parse_qs + from pgoapi.auth import Auth +from pgoapi.utilities import get_time +from pgoapi.exceptions import AuthException class AuthPtc(Auth): @@ -46,9 +53,11 @@ def __init__(self): self._session = requests.session() self._session.verify = True - def login(self, username, password): + def user_login(self, username, password): + self.log.info('PTC User Login for: {}'.format(username)) - self.log.info('Login for: %s', username) + if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): + raise AuthException("Username/password not correctly specified") head = {'User-Agent': 'niantic'} r = self._session.get(self.PTC_LOGIN_URL, headers=head) @@ -63,11 +72,12 @@ def login(self, username, password): 'password': password, } except ValueError as e: - self.log.error('Field missing in response: %s' % e) + self.log.error('PTC User Login Error - Field missing in response: %s', e) return False except KeyError as e: - self.log.error('Field missing in response.content: %s' % e) + self.log.error('PTC User Login Error - Field missing in response.content: %s', e) return False + r1 = self._session.post(self.PTC_LOGIN_URL, data=data, headers=head) ticket = None @@ -77,30 +87,62 @@ def login(self, username, password): try: self.log.error('Could not retrieve token: %s', r1.json()['errors'][0]) except Exception as e: - self.log.error('Could not retrieve token! (%s)', str(e)) + self.log.error('Could not retrieve token! (%s)', e) return False - data1 = { - 'client_id': 'mobile-app_pokemon-go', - 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', - 'client_secret': self.PTC_LOGIN_CLIENT_SECRET, - 'grant_type': 'refresh_token', - 'code': ticket, - } - - r2 = self._session.post(self.PTC_LOGIN_OAUTH, data=data1) - access_token = re.sub('&expires.*', '', r2.content.decode('utf-8')) - access_token = re.sub('.*access_token=', '', access_token) - - if '-sso.pokemon.com' in access_token: - self.log.info('PTC Login successful') - self.log.debug('PTC Session Token: %s', access_token[:25]) - self._auth_token = access_token + self._refresh_token = ticket + self.log.info('PTC User Login successful.') + + self.get_access_token() + + def set_refresh_token(self, refresh_token): + self.log.info('PTC Refresh Token provided by user') + self._refresh_token = refresh_token + + def get_access_token(self, force_refresh = False): + token_validity = self.check_access_token() + + if token_validity is True and force_refresh is False: + self.log.debug('Using cached PTC Access Token') + return self._access_token else: - self.log.info('Seems not to be a PTC Session Token... login failed :(') - return False - - self._login = True - - return True + if force_refresh: + self.log.info('Forced request of PTC Access Token!') + else: + self.log.info('Request PTC Access Token...') + + data1 = { + 'client_id': 'mobile-app_pokemon-go', + 'redirect_uri': 'https://www.nianticlabs.com/pokemongo/error', + 'client_secret': self.PTC_LOGIN_CLIENT_SECRET, + 'grant_type': 'refresh_token', + 'code': self._refresh_token, + } + + r2 = self._session.post(self.PTC_LOGIN_OAUTH, data=data1) + + qs = r2.content.decode('utf-8') + token_data = parse_qs(qs) + + access_token = token_data.get('access_token', None) + if access_token is not None: + self._access_token = access_token[0] + + now_s = get_time() + expires = int(token_data.get('expires', [0])[0]) + if expires > 0: + self._access_token_expiry = expires + now_s + else: + self._access_token_expiry = 0 + + self._login = True + + self.log.info('PTC Access Token successfully retrieved.') + self.log.debug('PTC Access Token: %s...', self._access_token[:25]) + else: + self._access_token = None + self._login = False + raise AuthException("Could not retrieve a PTC Access Token") + + diff --git a/pgoapi/exceptions.py b/pgoapi/exceptions.py index df539df6..162d8bdc 100755 --- a/pgoapi/exceptions.py +++ b/pgoapi/exceptions.py @@ -48,4 +48,17 @@ class ServerSideAccessForbiddenException(Exception): pass class UnexpectedResponseException(Exception): - pass \ No newline at end of file + pass + +class AuthTokenExpiredException(Exception): + pass + +class ServerApiEndpointRedirectException(Exception): + def __init__(self): + self._api_endpoint = None + + def get_redirected_endpoint(self): + return self._api_endpoint + + def set_redirected_endpoint(self, api_endpoint): + self._api_endpoint = api_endpoint diff --git a/pgoapi/pgoapi.py b/pgoapi/pgoapi.py index 03ba6eaf..374ad8de 100755 --- a/pgoapi/pgoapi.py +++ b/pgoapi/pgoapi.py @@ -34,33 +34,61 @@ from pgoapi.rpc_api import RpcApi from pgoapi.auth_ptc import AuthPtc from pgoapi.auth_google import AuthGoogle -from pgoapi.exceptions import AuthException, NotLoggedInException, ServerBusyOrOfflineException, NoPlayerPositionSetException, EmptySubrequestChainException +from pgoapi.utilities import parse_api_endpoint +from pgoapi.exceptions import AuthException, NotLoggedInException, ServerBusyOrOfflineException, NoPlayerPositionSetException, EmptySubrequestChainException, AuthTokenExpiredException, ServerApiEndpointRedirectException, UnexpectedResponseException from . import protos from POGOProtos.Networking.Requests_pb2 import RequestType logger = logging.getLogger(__name__) -class PGoApi: - def __init__(self): +class PGoApi: + def __init__(self, provider=None, oauth2_refresh_token=None, username=None, password=None, position_lat=None, position_lng=None, position_alt=None, proxy_config=None): self.set_logger() + self.log.info('%s v%s - %s', __title__, __version__, __copyright__) self._auth_provider = None - self._api_endpoint = 'https://pgorelease.nianticlabs.com/plfe/rpc' + if provider is not None and ((username is not None and password is not None) or (oauth2_refresh_token is not None)): + self.set_authentication(provider, oauth2_refresh_token, username, password) - self._position_lat = None - self._position_lng = None - self._position_alt = None + self.set_api_endpoint("pgorelease.nianticlabs.com/plfe") - self.log.info('%s v%s - %s', __title__, __version__, __copyright__ ) + self._position_lat = position_lat + self._position_lng = position_lng + self._position_alt = position_alt - def set_logger(self, logger = None): + self._signature_lib = None + + self._session = requests.session() + self._session.headers.update({'User-Agent': 'Niantic App'}) + self._session.verify = True + + if proxy_config is not None: + self._session.proxies = proxy_config + + def set_logger(self, logger=None): self.log = logger or logging.getLogger(__name__) - - def get_api_endpoint(self): - return self._api_endpoint + + def set_authentication(self, provider=None, oauth2_refresh_token=None, username=None, password=None): + if provider == 'ptc': + self._auth_provider = AuthPtc() + elif provider == 'google': + self._auth_provider = AuthGoogle() + elif provider is None: + self._auth_provider = None + else: + raise AuthException("Invalid authentication provider - only ptc/google available.") + + self.log.debug('Auth provider: %s', provider) + + if oauth2_refresh_token is not None: + self._auth_provider.set_refresh_token(oauth2_refresh_token) + elif username is not None and password is not None: + self._auth_provider.user_login(username, password) + else: + raise AuthException("Invalid Credential Input - Please provide username/password or an oauth2 refresh token") def get_position(self): return (self._position_lat, self._position_lng, self._position_alt) @@ -71,91 +99,103 @@ def set_position(self, lat, lng, alt): self._position_lat = lat self._position_lng = lng self._position_alt = alt - - def create_request(self): - request = PGoApiRequest(self._api_endpoint, self._auth_provider, self._position_lat, self._position_lng, self._position_alt) + + def set_proxy(self, proxy_config): + self._session.proxies = proxy_config + + def get_api_endpoint(self): + return self._api_endpoint + + def set_api_endpoint(self, api_url): + if api_url.startswith("https"): + self._api_endpoint = api_url + else: + self._api_endpoint = parse_api_endpoint(api_url) + + def get_auth_provider(self): + return self._auth_provider + + def create_request(self): + request = PGoApiRequest(self, self._position_lat, self._position_lng, + self._position_alt) return request + def activate_signature(self, lib_path): + self._signature_lib = lib_path + + def get_signature_lib(self): + return self._signature_lib + def __getattr__(self, func): - def function(**kwargs): request = self.create_request() - getattr(request, func)( _call_direct = True, **kwargs ) + getattr(request, func)(_call_direct=True, **kwargs ) return request.call() - if func.upper() in RequestType.keys(): + if func.upper() in RequestType.keys(): return function else: raise AttributeError - - def login(self, provider, username, password, lat = None, lng = None, alt = None, app_simulation = True): - if (lat is not None) and (lng is not None) and (alt is not None): - self._position_lat = lat - self._position_lng = lng - self._position_alt = alt + def app_simulation_login(self): + self.log.info('Starting RPC login sequence (app simulation)') - if not isinstance(username, six.string_types) or not isinstance(password, six.string_types): - raise AuthException("Username/password not correctly specified") + # making a standard call, like it is also done by the client + request = self.create_request() - if provider == 'ptc': - self._auth_provider = AuthPtc() - elif provider == 'google': - self._auth_provider = AuthGoogle() - else: - raise AuthException("Invalid authentication provider - only ptc/google available.") + request.get_player() + request.get_hatched_eggs() + request.get_inventory() + request.check_awarded_badges() + request.download_settings(hash="54b359c97e46900f87211ef6e6dd0b7f2a3ea1f5") - self.log.debug('Auth provider: %s', provider) + response = request.call() - if not self._auth_provider.login(username, password): - self.log.info('Login process failed') - return False + self.log.info('Finished RPC login sequence (app simulation)') - if app_simulation: - self.log.info('Starting RPC login sequence (app simulation)') + return response - # making a standard call, like it is also done by the client - request = self.create_request() + """ + The login function is not needed anymore but still in the code for backward compatibility" + """ + def login(self, provider, username, password, lat=None, lng=None, alt=None, app_simulation=True): - request.get_player() - request.get_hatched_eggs() - request.get_inventory() - request.check_awarded_badges() - request.download_settings(hash="05daf51635c82611d1aac95c0b051d3ec088a930") + if lat is not None and lng is not None and alt is not None: + self._position_lat = lat + self._position_lng = lng + self._position_alt = alt - response = request.call() + try: + self.set_authentication(provider, username=username, password=password) + except AuthException as e: + self.log.error('Login process failed: %s', e) + return False + + if app_simulation: + response = self.app_simulation_login() else: self.log.info('Starting minimal RPC login sequence') response = self.get_player() + self.log.info('Finished minimal RPC login sequence') if not response: self.log.info('Login failed!') return False - if 'api_url' in response: - self._api_endpoint = ('https://{}/rpc'.format(response['api_url'])) - self.log.debug('Setting API endpoint to: %s', self._api_endpoint) - else: - self.log.error('Login failed - unexpected server response!') - return False - - if app_simulation: - self.log.info('Finished RPC login sequence (app simulation)') - else: - self.log.info('Finished minimal RPC login sequence') - self.log.info('Login process completed') return True - + class PGoApiRequest: - def __init__(self, api_endpoint, auth_provider, position_lat, position_lng, position_alt): + def __init__(self, parent, position_lat, position_lng, position_alt): self.log = logging.getLogger(__name__) - """ Inherit necessary parameters """ - self._api_endpoint = api_endpoint - self._auth_provider = auth_provider + self.__parent__ = parent + + """ Inherit necessary parameters from parent """ + self._api_endpoint = self.__parent__.get_api_endpoint() + self._auth_provider = self.__parent__.get_auth_provider() self._position_lat = position_lat self._position_lng = position_lng @@ -166,7 +206,7 @@ def __init__(self, api_endpoint, auth_provider, position_lat, position_lng, posi def call(self): if not self._req_method_list: raise EmptySubrequestChainException() - + if (self._position_lat is None) or (self._position_lng is None) or (self._position_alt is None): raise NoPlayerPositionSetException() @@ -175,13 +215,52 @@ def call(self): return NotLoggedInException() request = RpcApi(self._auth_provider) + request._session = self.__parent__._session + + lib_path = self.__parent__.get_signature_lib() + if lib_path is not None: + request.activate_signature(lib_path) self.log.info('Execution of RPC') response = None - try: - response = request.request(self._api_endpoint, self._req_method_list, self.get_position()) - except ServerBusyOrOfflineException as e: - self.log.info('Server seems to be busy or offline - try again!') + + execute = True + while execute: + execute = False + + try: + response = request.request(self._api_endpoint, self._req_method_list, self.get_position()) + except AuthTokenExpiredException as e: + """ + This exception only occures if the OAUTH service provider (google/ptc) didn't send any expiration date + so that we are assuming, that the access_token is always valid until the API server states differently. + """ + try: + self.log.info('Access Token rejected! Requesting new one...') + self._auth_provider.get_access_token(force_refresh=True) + except: + error = 'Request for new Access Token failed! Logged out...' + self.log.error(error) + raise NotLoggedInException(error) + + """ reexecute the call""" + execute = True + except ServerApiEndpointRedirectException as e: + self.log.info('API Endpoint redirect... re-execution of call') + new_api_endpoint = e.get_redirected_endpoint() + + self._api_endpoint = parse_api_endpoint(new_api_endpoint) + self.__parent__.set_api_endpoint(self._api_endpoint) + + """ reexecute the call""" + execute = True + except ServerBusyOrOfflineException as e: + """ no execute = True here, as API retries on HTTP level should be done on a lower level, e.g. in rpc_api """ + self.log.info('Server seems to be busy or offline - try again!') + self.log.debug('ServerBusyOrOfflineException details: %s', e) + except UnexpectedResponseException as e: + self.log.error('Unexpected server response!') + raise # cleanup after call execution self.log.info('Cleanup of request!') @@ -223,7 +302,7 @@ def function(**kwargs): return self - if func.upper() in RequestType.keys(): + if func.upper() in RequestType.keys(): return function else: raise AttributeError diff --git a/pgoapi/protos/Signature.proto b/pgoapi/protos/Signature.proto new file mode 100644 index 00000000..7bc0d4eb --- /dev/null +++ b/pgoapi/protos/Signature.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; + +message Signature { + + message LocationFix { + string provider = 1; // "network", "gps", "fused", possibly others + uint64 timestamp_since_start = 2; // in ms + float latitude = 13; + float longitude = 14; + + // ??? shows up in struct, dunno where these go + // float device_speed; + // float device_course; + float horizontal_accuracy = 20; // iOS only? (range seems to be -1 to +1) + float altitude = 21; + float vertical_accuracy = 22; // iOS only? (range seems to be ~10-12) + uint64 provider_status = 26; // Usually 3 (possibly GPS status: 1 = no fix, 2 = acquiring/inaccurate, 3 = fix acquired) + // On iOS there are some LocationFixes with unk26=1 and everything else empty + uint32 floor = 27; // No idea what this is, seems to be optional + uint64 location_type = 28; // Always 1 (if there is data at all) + } + + // don't really care about this since we're not using it + message AndroidGpsInfo { + uint64 time_to_fix = 1; + repeated int32 satellites_prn = 2; + repeated float snr = 3; + repeated float azimuth = 4; + repeated float elevation = 5; + repeated bool has_almanac = 6; + repeated bool has_ephemeris = 7; + repeated bool used_in_fix = 8; + } + + message SensorInfo { + uint64 timestamp_snapshot = 1; // in ms + double magnetometer_x = 3; + double magnetometer_y = 4; + double magnetometer_z = 5; + double angle_normalized_x = 6; + double angle_normalized_y = 7; + double angle_normalized_z = 8; + double accel_raw_x = 10; + double accel_raw_y = 11; + double accel_raw_z = 12; + double gyroscope_raw_x = 13; + double gyroscope_raw_y = 14; + double gyroscope_raw_z = 15; + double accel_normalized_x = 16; + double accel_normalized_y = 17; + double accel_normalized_z = 18; + uint64 accelerometer_axes = 19; // Always 3 + } + + message DeviceInfo { + string device_id = 1; // Hex string + string android_board_name = 2; + string android_bootloader = 3; + string device_brand = 4; // On Android: product.brand + string device_model = 5; // On Android: product.device + string device_model_identifier = 6; // Android only, build.display.id + string device_model_boot = 7; // On Android: boot.hardware + string hardware_manufacturer = 8; // On Android: product.manufacturer + string hardware_model = 9; // On Android: product.model + string firmware_brand = 10; // On Android: product.name, on iOS: "iPhone OS" + string firmware_tags = 12; // Android only, build.tags + string firmware_type = 13; // On Android: build.type, on iOS instead: iOS version + string firmware_fingerprint = 14; // Android only, build.fingerprint + } + + message ActivityStatus { + // all of these had 1 as their value + uint64 start_time_ms = 1; + bool unknown_status = 2; + bool walking = 3; + bool running = 4; + bool stationary = 5; + bool automotive = 6; + bool tilting = 7; + bool cycling = 8; + bytes status = 9; + } + + uint64 timestamp_since_start = 2; // in ms + repeated LocationFix location_fix = 4; + AndroidGpsInfo gps_info = 5; + SensorInfo sensor_info = 7; + DeviceInfo device_info = 8; + ActivityStatus activity_status = 9; + uint32 location_hash1 = 10; // Location1 hashed based on the auth_token - xxHash32 + uint32 location_hash2 = 20; // Location2 hashed based on the auth_token - xxHash32 + bytes unk22 = 22; // possibly replay check. Generation unknown but pointed to by 0001B8614 + uint64 timestamp = 23; // epoch timestamp in ms + repeated uint64 request_hash = 24; // hashes of each request message in a hashArray - xxhash64 + + // Addresses for the corresponding hash functions: + // xxHash32 00054D28 + // xxhash64 000546C8 - Feeds into 00053D40 + +} diff --git a/pgoapi/protos/Signature_pb2.py b/pgoapi/protos/Signature_pb2.py new file mode 100644 index 00000000..49b0ebe7 --- /dev/null +++ b/pgoapi/protos/Signature_pb2.py @@ -0,0 +1,703 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: Signature.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='Signature.proto', + package='', + syntax='proto3', + serialized_pb=_b('\n\x0fSignature.proto\"\xf6\r\n\tSignature\x12\x1d\n\x15timestamp_since_start\x18\x02 \x01(\x04\x12,\n\x0clocation_fix\x18\x04 \x03(\x0b\x32\x16.Signature.LocationFix\x12+\n\x08gps_info\x18\x05 \x01(\x0b\x32\x19.Signature.AndroidGpsInfo\x12*\n\x0bsensor_info\x18\x07 \x01(\x0b\x32\x15.Signature.SensorInfo\x12*\n\x0b\x64\x65vice_info\x18\x08 \x01(\x0b\x32\x15.Signature.DeviceInfo\x12\x32\n\x0f\x61\x63tivity_status\x18\t \x01(\x0b\x32\x19.Signature.ActivityStatus\x12\x16\n\x0elocation_hash1\x18\n \x01(\r\x12\x16\n\x0elocation_hash2\x18\x14 \x01(\r\x12\r\n\x05unk22\x18\x16 \x01(\x0c\x12\x11\n\ttimestamp\x18\x17 \x01(\x04\x12\x14\n\x0crequest_hash\x18\x18 \x03(\x04\x1a\xec\x01\n\x0bLocationFix\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\x1d\n\x15timestamp_since_start\x18\x02 \x01(\x04\x12\x10\n\x08latitude\x18\r \x01(\x02\x12\x11\n\tlongitude\x18\x0e \x01(\x02\x12\x1b\n\x13horizontal_accuracy\x18\x14 \x01(\x02\x12\x10\n\x08\x61ltitude\x18\x15 \x01(\x02\x12\x19\n\x11vertical_accuracy\x18\x16 \x01(\x02\x12\x17\n\x0fprovider_status\x18\x1a \x01(\x04\x12\r\n\x05\x66loor\x18\x1b \x01(\r\x12\x15\n\rlocation_type\x18\x1c \x01(\x04\x1a\xaf\x01\n\x0e\x41ndroidGpsInfo\x12\x13\n\x0btime_to_fix\x18\x01 \x01(\x04\x12\x16\n\x0esatellites_prn\x18\x02 \x03(\x05\x12\x0b\n\x03snr\x18\x03 \x03(\x02\x12\x0f\n\x07\x61zimuth\x18\x04 \x03(\x02\x12\x11\n\televation\x18\x05 \x03(\x02\x12\x13\n\x0bhas_almanac\x18\x06 \x03(\x08\x12\x15\n\rhas_ephemeris\x18\x07 \x03(\x08\x12\x13\n\x0bused_in_fix\x18\x08 \x03(\x08\x1a\xbe\x03\n\nSensorInfo\x12\x1a\n\x12timestamp_snapshot\x18\x01 \x01(\x04\x12\x16\n\x0emagnetometer_x\x18\x03 \x01(\x01\x12\x16\n\x0emagnetometer_y\x18\x04 \x01(\x01\x12\x16\n\x0emagnetometer_z\x18\x05 \x01(\x01\x12\x1a\n\x12\x61ngle_normalized_x\x18\x06 \x01(\x01\x12\x1a\n\x12\x61ngle_normalized_y\x18\x07 \x01(\x01\x12\x1a\n\x12\x61ngle_normalized_z\x18\x08 \x01(\x01\x12\x13\n\x0b\x61\x63\x63\x65l_raw_x\x18\n \x01(\x01\x12\x13\n\x0b\x61\x63\x63\x65l_raw_y\x18\x0b \x01(\x01\x12\x13\n\x0b\x61\x63\x63\x65l_raw_z\x18\x0c \x01(\x01\x12\x17\n\x0fgyroscope_raw_x\x18\r \x01(\x01\x12\x17\n\x0fgyroscope_raw_y\x18\x0e \x01(\x01\x12\x17\n\x0fgyroscope_raw_z\x18\x0f \x01(\x01\x12\x1a\n\x12\x61\x63\x63\x65l_normalized_x\x18\x10 \x01(\x01\x12\x1a\n\x12\x61\x63\x63\x65l_normalized_y\x18\x11 \x01(\x01\x12\x1a\n\x12\x61\x63\x63\x65l_normalized_z\x18\x12 \x01(\x01\x12\x1a\n\x12\x61\x63\x63\x65lerometer_axes\x18\x13 \x01(\x04\x1a\xda\x02\n\nDeviceInfo\x12\x11\n\tdevice_id\x18\x01 \x01(\t\x12\x1a\n\x12\x61ndroid_board_name\x18\x02 \x01(\t\x12\x1a\n\x12\x61ndroid_bootloader\x18\x03 \x01(\t\x12\x14\n\x0c\x64\x65vice_brand\x18\x04 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x05 \x01(\t\x12\x1f\n\x17\x64\x65vice_model_identifier\x18\x06 \x01(\t\x12\x19\n\x11\x64\x65vice_model_boot\x18\x07 \x01(\t\x12\x1d\n\x15hardware_manufacturer\x18\x08 \x01(\t\x12\x16\n\x0ehardware_model\x18\t \x01(\t\x12\x16\n\x0e\x66irmware_brand\x18\n \x01(\t\x12\x15\n\rfirmware_tags\x18\x0c \x01(\t\x12\x15\n\rfirmware_type\x18\r \x01(\t\x12\x1c\n\x14\x66irmware_fingerprint\x18\x0e \x01(\t\x1a\xbb\x01\n\x0e\x41\x63tivityStatus\x12\x15\n\rstart_time_ms\x18\x01 \x01(\x04\x12\x16\n\x0eunknown_status\x18\x02 \x01(\x08\x12\x0f\n\x07walking\x18\x03 \x01(\x08\x12\x0f\n\x07running\x18\x04 \x01(\x08\x12\x12\n\nstationary\x18\x05 \x01(\x08\x12\x12\n\nautomotive\x18\x06 \x01(\x08\x12\x0f\n\x07tilting\x18\x07 \x01(\x08\x12\x0f\n\x07\x63ycling\x18\x08 \x01(\x08\x12\x0e\n\x06status\x18\t \x01(\x0c\x62\x06proto3') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_SIGNATURE_LOCATIONFIX = _descriptor.Descriptor( + name='LocationFix', + full_name='Signature.LocationFix', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='provider', full_name='Signature.LocationFix.provider', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='timestamp_since_start', full_name='Signature.LocationFix.timestamp_since_start', index=1, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='latitude', full_name='Signature.LocationFix.latitude', index=2, + number=13, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='longitude', full_name='Signature.LocationFix.longitude', index=3, + number=14, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='horizontal_accuracy', full_name='Signature.LocationFix.horizontal_accuracy', index=4, + number=20, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='altitude', full_name='Signature.LocationFix.altitude', index=5, + number=21, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='vertical_accuracy', full_name='Signature.LocationFix.vertical_accuracy', index=6, + number=22, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='provider_status', full_name='Signature.LocationFix.provider_status', index=7, + number=26, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='floor', full_name='Signature.LocationFix.floor', index=8, + number=27, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='location_type', full_name='Signature.LocationFix.location_type', index=9, + number=28, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=400, + serialized_end=636, +) + +_SIGNATURE_ANDROIDGPSINFO = _descriptor.Descriptor( + name='AndroidGpsInfo', + full_name='Signature.AndroidGpsInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='time_to_fix', full_name='Signature.AndroidGpsInfo.time_to_fix', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='satellites_prn', full_name='Signature.AndroidGpsInfo.satellites_prn', index=1, + number=2, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='snr', full_name='Signature.AndroidGpsInfo.snr', index=2, + number=3, type=2, cpp_type=6, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='azimuth', full_name='Signature.AndroidGpsInfo.azimuth', index=3, + number=4, type=2, cpp_type=6, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='elevation', full_name='Signature.AndroidGpsInfo.elevation', index=4, + number=5, type=2, cpp_type=6, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='has_almanac', full_name='Signature.AndroidGpsInfo.has_almanac', index=5, + number=6, type=8, cpp_type=7, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='has_ephemeris', full_name='Signature.AndroidGpsInfo.has_ephemeris', index=6, + number=7, type=8, cpp_type=7, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='used_in_fix', full_name='Signature.AndroidGpsInfo.used_in_fix', index=7, + number=8, type=8, cpp_type=7, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=639, + serialized_end=814, +) + +_SIGNATURE_SENSORINFO = _descriptor.Descriptor( + name='SensorInfo', + full_name='Signature.SensorInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp_snapshot', full_name='Signature.SensorInfo.timestamp_snapshot', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='magnetometer_x', full_name='Signature.SensorInfo.magnetometer_x', index=1, + number=3, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='magnetometer_y', full_name='Signature.SensorInfo.magnetometer_y', index=2, + number=4, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='magnetometer_z', full_name='Signature.SensorInfo.magnetometer_z', index=3, + number=5, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='angle_normalized_x', full_name='Signature.SensorInfo.angle_normalized_x', index=4, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='angle_normalized_y', full_name='Signature.SensorInfo.angle_normalized_y', index=5, + number=7, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='angle_normalized_z', full_name='Signature.SensorInfo.angle_normalized_z', index=6, + number=8, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_raw_x', full_name='Signature.SensorInfo.accel_raw_x', index=7, + number=10, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_raw_y', full_name='Signature.SensorInfo.accel_raw_y', index=8, + number=11, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_raw_z', full_name='Signature.SensorInfo.accel_raw_z', index=9, + number=12, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='gyroscope_raw_x', full_name='Signature.SensorInfo.gyroscope_raw_x', index=10, + number=13, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='gyroscope_raw_y', full_name='Signature.SensorInfo.gyroscope_raw_y', index=11, + number=14, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='gyroscope_raw_z', full_name='Signature.SensorInfo.gyroscope_raw_z', index=12, + number=15, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_normalized_x', full_name='Signature.SensorInfo.accel_normalized_x', index=13, + number=16, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_normalized_y', full_name='Signature.SensorInfo.accel_normalized_y', index=14, + number=17, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accel_normalized_z', full_name='Signature.SensorInfo.accel_normalized_z', index=15, + number=18, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='accelerometer_axes', full_name='Signature.SensorInfo.accelerometer_axes', index=16, + number=19, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=817, + serialized_end=1263, +) + +_SIGNATURE_DEVICEINFO = _descriptor.Descriptor( + name='DeviceInfo', + full_name='Signature.DeviceInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='device_id', full_name='Signature.DeviceInfo.device_id', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='android_board_name', full_name='Signature.DeviceInfo.android_board_name', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='android_bootloader', full_name='Signature.DeviceInfo.android_bootloader', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='device_brand', full_name='Signature.DeviceInfo.device_brand', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='device_model', full_name='Signature.DeviceInfo.device_model', index=4, + number=5, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='device_model_identifier', full_name='Signature.DeviceInfo.device_model_identifier', index=5, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='device_model_boot', full_name='Signature.DeviceInfo.device_model_boot', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='hardware_manufacturer', full_name='Signature.DeviceInfo.hardware_manufacturer', index=7, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='hardware_model', full_name='Signature.DeviceInfo.hardware_model', index=8, + number=9, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='firmware_brand', full_name='Signature.DeviceInfo.firmware_brand', index=9, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='firmware_tags', full_name='Signature.DeviceInfo.firmware_tags', index=10, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='firmware_type', full_name='Signature.DeviceInfo.firmware_type', index=11, + number=13, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='firmware_fingerprint', full_name='Signature.DeviceInfo.firmware_fingerprint', index=12, + number=14, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1266, + serialized_end=1612, +) + +_SIGNATURE_ACTIVITYSTATUS = _descriptor.Descriptor( + name='ActivityStatus', + full_name='Signature.ActivityStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='start_time_ms', full_name='Signature.ActivityStatus.start_time_ms', index=0, + number=1, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='unknown_status', full_name='Signature.ActivityStatus.unknown_status', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='walking', full_name='Signature.ActivityStatus.walking', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='running', full_name='Signature.ActivityStatus.running', index=3, + number=4, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='stationary', full_name='Signature.ActivityStatus.stationary', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='automotive', full_name='Signature.ActivityStatus.automotive', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='tilting', full_name='Signature.ActivityStatus.tilting', index=6, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='cycling', full_name='Signature.ActivityStatus.cycling', index=7, + number=8, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='status', full_name='Signature.ActivityStatus.status', index=8, + number=9, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1615, + serialized_end=1802, +) + +_SIGNATURE = _descriptor.Descriptor( + name='Signature', + full_name='Signature', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp_since_start', full_name='Signature.timestamp_since_start', index=0, + number=2, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='location_fix', full_name='Signature.location_fix', index=1, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='gps_info', full_name='Signature.gps_info', index=2, + number=5, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='sensor_info', full_name='Signature.sensor_info', index=3, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='device_info', full_name='Signature.device_info', index=4, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='activity_status', full_name='Signature.activity_status', index=5, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='location_hash1', full_name='Signature.location_hash1', index=6, + number=10, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='location_hash2', full_name='Signature.location_hash2', index=7, + number=20, type=13, cpp_type=3, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='unk22', full_name='Signature.unk22', index=8, + number=22, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='timestamp', full_name='Signature.timestamp', index=9, + number=23, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='request_hash', full_name='Signature.request_hash', index=10, + number=24, type=4, cpp_type=4, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[_SIGNATURE_LOCATIONFIX, _SIGNATURE_ANDROIDGPSINFO, _SIGNATURE_SENSORINFO, _SIGNATURE_DEVICEINFO, _SIGNATURE_ACTIVITYSTATUS, ], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=20, + serialized_end=1802, +) + +_SIGNATURE_LOCATIONFIX.containing_type = _SIGNATURE +_SIGNATURE_ANDROIDGPSINFO.containing_type = _SIGNATURE +_SIGNATURE_SENSORINFO.containing_type = _SIGNATURE +_SIGNATURE_DEVICEINFO.containing_type = _SIGNATURE +_SIGNATURE_ACTIVITYSTATUS.containing_type = _SIGNATURE +_SIGNATURE.fields_by_name['location_fix'].message_type = _SIGNATURE_LOCATIONFIX +_SIGNATURE.fields_by_name['gps_info'].message_type = _SIGNATURE_ANDROIDGPSINFO +_SIGNATURE.fields_by_name['sensor_info'].message_type = _SIGNATURE_SENSORINFO +_SIGNATURE.fields_by_name['device_info'].message_type = _SIGNATURE_DEVICEINFO +_SIGNATURE.fields_by_name['activity_status'].message_type = _SIGNATURE_ACTIVITYSTATUS +DESCRIPTOR.message_types_by_name['Signature'] = _SIGNATURE + +Signature = _reflection.GeneratedProtocolMessageType('Signature', (_message.Message,), dict( + + LocationFix = _reflection.GeneratedProtocolMessageType('LocationFix', (_message.Message,), dict( + DESCRIPTOR = _SIGNATURE_LOCATIONFIX, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature.LocationFix) + )) + , + + AndroidGpsInfo = _reflection.GeneratedProtocolMessageType('AndroidGpsInfo', (_message.Message,), dict( + DESCRIPTOR = _SIGNATURE_ANDROIDGPSINFO, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature.AndroidGpsInfo) + )) + , + + SensorInfo = _reflection.GeneratedProtocolMessageType('SensorInfo', (_message.Message,), dict( + DESCRIPTOR = _SIGNATURE_SENSORINFO, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature.SensorInfo) + )) + , + + DeviceInfo = _reflection.GeneratedProtocolMessageType('DeviceInfo', (_message.Message,), dict( + DESCRIPTOR = _SIGNATURE_DEVICEINFO, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature.DeviceInfo) + )) + , + + ActivityStatus = _reflection.GeneratedProtocolMessageType('ActivityStatus', (_message.Message,), dict( + DESCRIPTOR = _SIGNATURE_ACTIVITYSTATUS, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature.ActivityStatus) + )) + , + DESCRIPTOR = _SIGNATURE, + __module__ = 'Signature_pb2' + # @@protoc_insertion_point(class_scope:Signature) + )) +_sym_db.RegisterMessage(Signature) +_sym_db.RegisterMessage(Signature.LocationFix) +_sym_db.RegisterMessage(Signature.AndroidGpsInfo) +_sym_db.RegisterMessage(Signature.SensorInfo) +_sym_db.RegisterMessage(Signature.DeviceInfo) +_sym_db.RegisterMessage(Signature.ActivityStatus) + + +# @@protoc_insertion_point(module_scope) diff --git a/pgoapi/rpc_api.py b/pgoapi/rpc_api.py index 258dc79f..0c609377 100755 --- a/pgoapi/rpc_api.py +++ b/pgoapi/rpc_api.py @@ -25,44 +25,63 @@ from __future__ import absolute_import +import os import re +import time import base64 import random import logging import requests import subprocess +import six from google.protobuf import message from importlib import import_module + +import ctypes + from pgoapi.protobuf_to_dict import protobuf_to_dict -from pgoapi.exceptions import NotLoggedInException, ServerBusyOrOfflineException, ServerSideRequestThrottlingException, ServerSideAccessForbiddenException, UnexpectedResponseException -from pgoapi.utilities import f2i, h2f, to_camel_case, get_time_ms, get_format_time_diff +from pgoapi.exceptions import NotLoggedInException, ServerBusyOrOfflineException, ServerSideRequestThrottlingException, ServerSideAccessForbiddenException, UnexpectedResponseException, AuthTokenExpiredException, ServerApiEndpointRedirectException +from pgoapi.utilities import to_camel_case, get_time, get_format_time_diff, Rand48, long_to_bytes, generateLocation1, generateLocation2, generateRequestHash, f2i from . import protos from POGOProtos.Networking.Envelopes_pb2 import RequestEnvelope from POGOProtos.Networking.Envelopes_pb2 import ResponseEnvelope from POGOProtos.Networking.Requests_pb2 import RequestType +import Signature_pb2 + class RpcApi: RPC_ID = 0 + START_TIME = 0 def __init__(self, auth_provider): self.log = logging.getLogger(__name__) - self._session = requests.session() - self._session.headers.update({'User-Agent': 'Niantic App'}) - self._session.verify = True - self._auth_provider = auth_provider + """ mystic unknown6 - revolved by PokemonGoDev """ + self._signature_gen = False + self._signature_lib = None + + if RpcApi.START_TIME == 0: + RpcApi.START_TIME = get_time(ms=True) + if RpcApi.RPC_ID == 0: RpcApi.RPC_ID = int(random.random() * 10 ** 18) self.log.debug('Generated new random RPC Request id: %s', RpcApi.RPC_ID) + def activate_signature(self, lib_path): + try: + self._signature_gen = True + self._signature_lib = ctypes.cdll.LoadLibrary(lib_path) + except: + raise + def get_rpc_id(self): RpcApi.RPC_ID += 1 self.log.debug("Incremented RPC Request ID: %s", RpcApi.RPC_ID) @@ -89,9 +108,9 @@ def _make_rpc(self, endpoint, request_proto_plain): request_proto_serialized = request_proto_plain.SerializeToString() try: - http_response = self._session.post(endpoint, data=request_proto_serialized) - except requests.exceptions.ConnectionError as e: - raise ServerBusyOrOfflineException + http_response = self._session.post(endpoint, data=request_proto_serialized, timeout=30) + except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e: + raise ServerBusyOrOfflineException(e) return http_response @@ -107,15 +126,23 @@ def request(self, endpoint, subrequests, player_position): self.check_authentication(response_dict) - """ - some response validations """ - if isinstance(response_dict, dict) and 'status_code' in response_dict: - sc = response_dict['status_code'] - if sc == 102: - raise NotLoggedInException() - elif sc == 52: + some response validations + """ + if isinstance(response_dict, dict): + status_code = response_dict.get('status_code', None) + if status_code == 102: + raise AuthTokenExpiredException() + elif status_code == 52: raise ServerSideRequestThrottlingException("Request throttled by server... slow down man") + elif status_code == 53: + api_url = response_dict.get('api_url', None) + if api_url is not None: + exception = ServerApiEndpointRedirectException() + exception.set_redirected_endpoint(api_url) + raise exception + else: + raise UnexpectedResponseException() return response_dict @@ -130,15 +157,15 @@ def check_authentication(self, response_dict): self._auth_provider.set_ticket( [auth_ticket['expire_timestamp_ms'], base64.standard_b64decode(auth_ticket['start']), base64.standard_b64decode(auth_ticket['end'])]) - now_ms = get_time_ms() + now_ms = get_time(ms=True) h, m, s = get_format_time_diff(now_ms, auth_ticket['expire_timestamp_ms'], True) if had_ticket: - self.log.debug('Replacing old auth ticket with new one valid for %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, auth_ticket['expire_timestamp_ms']) + self.log.debug('Replacing old Session Ticket with new one valid for %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, auth_ticket['expire_timestamp_ms']) else: - self.log.debug('Received auth ticket valid for %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, auth_ticket['expire_timestamp_ms']) + self.log.debug('Received Session Ticket valid for %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, auth_ticket['expire_timestamp_ms']) - def _build_main_request(self, subrequests, player_position = None): + def _build_main_request(self, subrequests, player_position=None): self.log.debug('Generating main RPC request...') request = RequestEnvelope() @@ -148,22 +175,93 @@ def _build_main_request(self, subrequests, player_position = None): if player_position is not None: request.latitude, request.longitude, request.altitude = player_position + request.altitude = 8 # not as suspicious as 0 + + """ generate sub requests before signature generation """ + request = self._build_sub_requests(request, subrequests) + ticket = self._auth_provider.get_ticket() if ticket: - self.log.debug('Found auth ticket - using this instead of oauth token') + 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 auth ticket found - using oauth token') + 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_token() + 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._signature_gen: + sig = Signature_pb2.Signature() + + sig.location_hash1 = generateLocation1(ticket_serialized, request.latitude, request.longitude, request.altitude) + sig.location_hash2 = generateLocation2(request.latitude, request.longitude, request.altitude) + + for req in request.requests: + hash = generateRequestHash(ticket_serialized, req.SerializeToString()) + sig.request_hash.append(hash) + + sig.unk22 = os.urandom(32) + sig.timestamp = get_time(ms=True) + sig.timestamp_since_start = get_time(ms=True) - RpcApi.START_TIME + + signature_proto = sig.SerializeToString() + + u6 = request.unknown6.add() + u6.request_type = 6 + u6.unknown2.unknown1 = self._generate_signature(signature_proto) # unknown stuff request.unknown12 = 989 + self.log.debug('Generated protobuf request: \n\r%s', request) + + return request + + def _generate_signature(self, signature_plain, lib_path="encrypt.so"): + if self._signature_lib is None: + self.activate_signature(lib_path) + self._signature_lib.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte), ctypes.POINTER(ctypes.c_size_t)] + self._signature_lib.restype = ctypes.c_int + + iv = os.urandom(32) + + output_size = ctypes.c_size_t() + + self._signature_lib.encrypt(signature_plain, len(signature_plain), iv, 32, None, ctypes.byref(output_size)) + output = (ctypes.c_ubyte * output_size.value)() + self._signature_lib.encrypt(signature_plain, len(signature_plain), iv, 32, ctypes.byref(output), ctypes.byref(output_size)) + signature = b''.join(list(map(lambda x: six.int2byte(x), output))) + return signature + + def _build_main_request_orig(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 = self._build_sub_requests(request, subrequests) - self.log.debug('Generated protobuf request: \n\r%s', request ) + if player_position is not None: + request.latitude, request.longitude, request.altitude = player_position + + 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 + 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 + + # unknown stuff + request.unknown12 = 3352 + + self.log.debug('Generated protobuf request: \n\r%s', request) return request @@ -193,14 +291,14 @@ def _build_sub_requests(self, mainrequest, subrequest_list): 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, str(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, str(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) @@ -210,7 +308,7 @@ def _build_sub_requests(self, mainrequest, subrequest_list): 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, str(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 @@ -224,12 +322,13 @@ def _build_sub_requests(self, mainrequest, subrequest_list): return mainrequest - def _parse_main_response(self, response_raw, subrequests): self.log.debug('Parsing main RPC response...') if response_raw.status_code == 403: raise ServerSideAccessForbiddenException("Seems your IP Address is banned or something else went badly wrong...") + elif response_raw.status_code == 502: + raise ServerBusyOrOfflineException("502: Bad Gateway") elif response_raw.status_code != 200: error = 'Unexpected HTTP server response - needs 200 got {}'.format(response_raw.status_code) self.log.warning(error) @@ -244,7 +343,7 @@ def _parse_main_response(self, response_raw, subrequests): try: response_proto.ParseFromString(response_raw.content) except message.DecodeError as e: - self.log.warning('Could not parse response: %s', str(e)) + self.log.warning('Could not parse response: %s', e) return False self.log.debug('Protobuf structure of rpc response:\n\r%s', response_proto) @@ -262,10 +361,15 @@ def _parse_sub_responses(self, response_proto, subrequests_list, response_proto_ 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 + list_len = len(subrequests_list)-1 i = 0 for subresponse in response_proto.returns: if i > list_len: @@ -275,7 +379,7 @@ def _parse_sub_responses(self, response_proto, subrequests_list, response_proto_ if isinstance(request_entry, int): entry_id = request_entry else: - entry_id = list(request_entry.items())[0][0] + entry_id = list(request_entry.items())[0][0] entry_name = RequestType.Name(entry_id) proto_name = to_camel_case(entry_name.lower()) + 'Response' @@ -285,7 +389,7 @@ def _parse_sub_responses(self, response_proto, subrequests_list, response_proto_ subresponse_return = None try: - subresponse_extension = self.get_class(proto_classname)() + subresponse_extension = self.get_class(proto_classname)() except Exception as e: subresponse_extension = None error = 'Protobuf definition for {} not found'.format(proto_classname) @@ -293,7 +397,7 @@ def _parse_sub_responses(self, response_proto, subrequests_list, response_proto_ self.log.debug(error) if subresponse_extension: - try: + try: subresponse_extension.ParseFromString(subresponse) subresponse_return = protobuf_to_dict(subresponse_extension) except: diff --git a/pgoapi/utilities.py b/pgoapi/utilities.py index 28b77179..dd0ae440 100755 --- a/pgoapi/utilities.py +++ b/pgoapi/utilities.py @@ -26,9 +26,12 @@ import re import time import struct +import ctypes +import xxhash import logging from json import JSONEncoder +from binascii import unhexlify # other stuff from google.protobuf.internal import encoder @@ -37,11 +40,11 @@ log = logging.getLogger(__name__) -def f2i(float): - return struct.unpack('> 17 + def mrand(self): + n = self.next() >> 16 + if n & (1 << 31): + n -= 1 << 32 + return n + +def long_to_bytes (val, endianness='big'): + """ + Use :ref:`string formatting` and :func:`~binascii.unhexlify` to + convert ``val``, a :func:`long`, to a byte :func:`str`. + + :param long val: The value to pack + + :param str endianness: The endianness of the result. ``'big'`` for + big-endian, ``'little'`` for little-endian. + + If you want byte- and word-ordering to differ, you're on your own. + + Using :ref:`string formatting` lets us use Python's C innards. + """ + + # one (1) hex digit per four (4) bits + width = val.bit_length() + + # unhexlify wants an even multiple of eight (8) bits, but we don't + # want more digits than we need (hence the ternary-ish 'or') + width += 8 - ((width % 8) or 8) + + # format width specifier: four (4) bits per hex digit + fmt = '%%0%dx' % (width // 4) + + # prepend zero (0) to the width, to zero-pad the output + s = unhexlify(fmt % val) + + if endianness == 'little': + # see http://stackoverflow.com/a/931095/309233 + s = s[::-1] + + return s + + +def generateLocation1(authticket, lat, lng, alt): + firstHash = xxhash.xxh32(authticket, seed=0x1B845238).intdigest() + locationBytes = d2h(lat) + d2h(lng) + d2h(alt) + if not alt: + alt = "\x00\x00\x00\x00\x00\x00\x00\x00" + return xxhash.xxh32(locationBytes, seed=firstHash).intdigest() + +def generateLocation2(lat, lng, alt): + locationBytes = d2h(lat) + d2h(lng) + d2h(alt) + if not alt: + alt = "\x00\x00\x00\x00\x00\x00\x00\x00" + return xxhash.xxh32(locationBytes, seed=0x1B845238).intdigest() #Hash of location using static seed 0x1B845238 + + +def generateRequestHash(authticket, request): + firstHash = xxhash.xxh64(authticket, seed=0x1B845238).intdigest() + return xxhash.xxh64(request, seed=firstHash).intdigest() + + + +def d2h(f): + hex_str = f2h(f)[2:].replace('L','') + hex_str = ("0" * (len(hex_str) % 2)) + hex_str + return unhexlify(hex_str) diff --git a/pokecli.py b/pokecli.py index a402051e..aed79cee 100755 --- a/pokecli.py +++ b/pokecli.py @@ -60,9 +60,10 @@ def init_config(): parser.add_argument("-u", "--username", help="Username", required=required("username")) parser.add_argument("-p", "--password", help="Password") parser.add_argument("-l", "--location", help="Location", required=required("location")) + parser.add_argument("-e", "--encrypt", help="Encryption Library Path") parser.add_argument("-d", "--debug", help="Debug Mode", action='store_true') parser.add_argument("-t", "--test", help="Only parse the specified location", action='store_true') - parser.set_defaults(DEBUG=False, TEST=False) + parser.set_defaults(DEBUG=False, TEST=False, ENCRYPT="encrypt.dll") config = parser.parse_args() # Passed in arguments shoud trump @@ -116,24 +117,20 @@ def main(): # set player position on the earth api.set_position(*position) - if not api.login(config.auth_service, config.username, config.password, app_simulation = True): - return + # new authentication initialitation + api.set_authentication(provider = config.auth_service, username = config.username, password = config.password) - # get player profile call (single command example) - # ---------------------- - response_dict = api.get_player() - print('Response dictionary (get_player): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict))) + # provide the path for your encrypt dll + # Use libencrypt.so for *nix - # sleep due to server-side throttling - time.sleep(0.2) + api.activate_signature(config.encrypt) + + # print get maps object + cell_ids = util.get_cell_ids(position[0], position[1]) + timestamps = [0,] * len(cell_ids) + response_dict = api.get_map_objects(latitude =position[0], longitude = position[1], since_timestamp_ms = timestamps, cell_id = cell_ids) + print('Response dictionary (get_player): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict))) - # get player profile + inventory call (thread-safe/chaining example) - # ---------------------- - req = api.create_request() - req.get_player() - req.get_inventory() - response_dict = req.call() - print('Response dictionary (get_player + get_inventory): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict))) if __name__ == '__main__': main() diff --git a/requirements.txt b/requirements.txt index 872071bb..62c7a17a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,7 @@ protobuf>=3.0.0a3 requests==2.10.0 s2sphere==0.2.4 gpsoauth==0.3.0 +future six +xxhash +requests[socks] diff --git a/scripts/accept-tos.py b/scripts/accept-tos.py new file mode 100644 index 00000000..152a7486 --- /dev/null +++ b/scripts/accept-tos.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +"""accept-tos.py: Example script to accept in-game Terms of Service""" + +from pgoapi import PGoApi +from pgoapi.utilities import f2i +from pgoapi import utilities as util +from pgoapi.exceptions import AuthException +import pprint +import time +import threading + +def accept_tos(username, password, auth='ptc'): + api = PGoApi() + api.set_position(40.7127837, -74.005941, 0.0) + api.login(auth, username, password) + time.sleep(2) + req = api.create_request() + req.mark_tutorial_complete(tutorials_completed = 0, send_marketing_emails = False, send_push_notifications = False) + response = req.call() + print('Accepted Terms of Service for {}'.format(username)) + #print('Response dictionary: \r\n{}'.format(pprint.PrettyPrinter(indent=4).pformat(response))) + +"""auth service defaults to ptc if not given""" + +accept_tos('username', 'password') +accept_tos('username2', 'password', 'ptc') +accept_tos('username3', 'password', 'google')