def _fetch_channel_sid(self): """Request a new session ID for the push channel. Raises hangups.NetworkError. """ logger.info('Requesting new session...') url = 'https://talkgadget.google.com{}bind'.format(self._channel_path) params = { 'VER': 8, 'clid': self._clid_param, 'ec': self._ec_param, 'RID': 81187, # Required if we want our client to be called "AChromeExtension": 'prop': self._prop_param, } try: res = yield from http_utils.fetch('post', url, cookies=self._cookies, params=params, data='count=0', connector=self._connector) except exceptions.NetworkError as e: raise exceptions.HangupsError( 'Failed to request SID: {}'.format(e)) # TODO: Re-write the function we're calling here to use a schema so we # can easily catch its failure. self._sid_param, self._email, self._header_client, self._gsessionid_param = ( _parse_sid_response(res.body)) logger.info('New SID: {}'.format(self._sid_param)) logger.info('New email: {}'.format(self._email)) logger.info('New client: {}'.format(self._header_client)) logger.info('New gsessionid: {}'.format(self._gsessionid_param))
def _initialize_chat(self): """Request push channel creation and initial chat data. Returns instance of InitialData. The response body is a HTML document containing a series of script tags containing JavaScript objects. We need to parse the objects to get at the data. """ try: res = yield from http_utils.fetch('get', CHAT_INIT_URL, cookies=self._cookies, params=CHAT_INIT_PARAMS, connector=self._connector) except exceptions.NetworkError as e: raise exceptions.HangupsError( 'Initialize chat request failed: {}'.format(e)) # Parse the response by using a regex to find all the JS objects, and # parsing them. Not everything will be parsable, but we don't care if # an object we don't need can't be parsed. data_dict = {} for data in CHAT_INIT_REGEX.findall(res.body.decode()): try: data = javascript.loads(data) # pylint: disable=invalid-sequence-index data_dict[data['key']] = data['data'] except ValueError as e: logger.debug( 'Failed to parse initialize chat object: {}\n{}'.format( e, data)) # Extract various values that we will need. try: self._api_key = data_dict['ds:7'][0][2] self._header_date = data_dict['ds:2'][0][4] self._header_version = data_dict['ds:2'][0][6] self._header_id = data_dict['ds:4'][0][7] self._channel_path = data_dict['ds:4'][0][1] self._clid = data_dict['ds:4'][0][7] self._channel_ec_param = data_dict['ds:4'][0][4] self._channel_prop_param = data_dict['ds:4'][0][5] _sync_timestamp = parsers.from_timestamp( data_dict['ds:21'][0][1][4]) except KeyError as e: raise exceptions.HangupsError('Failed to get initialize chat ' 'value: {}'.format(e)) # Parse the entity representing the current user. self_entity = schemas.CLIENT_GET_SELF_INFO_RESPONSE.parse( data_dict['ds:20'][0]).self_entity # Parse every existing conversation's state, including participants. initial_conv_states = schemas.CLIENT_CONVERSATION_STATE_LIST.parse( data_dict['ds:19'][0][3]) initial_conv_parts = [] for conv_state in initial_conv_states: initial_conv_parts.extend(conv_state.conversation.participant_data) # Parse the entities for the user's contacts (doesn't include users not # in contacts). If this fails, continue without the rest of the # entities. initial_entities = [] try: entities = schemas.INITIAL_CLIENT_ENTITIES.parse( data_dict['ds:21'][0]) except ValueError as e: logger.warning( 'Failed to parse initial client entities: {}'.format(e)) else: initial_entities.extend(entities.entities) initial_entities.extend(e.entity for e in itertools.chain( entities.group1.entity, entities.group2.entity, entities. group3.entity, entities.group4.entity, entities.group5.entity)) return InitialData(initial_conv_states, self_entity, initial_entities, initial_conv_parts, _sync_timestamp)
def _initialize_chat(self): """Request push channel creation and initial chat data. Returns instance of InitialData. The response body is a HTML document containing a series of script tags containing JavaScript objects. We need to parse the objects to get at the data. """ # We first need to fetch the 'pvt' token, which is required for the # initialization request (otherwise it will return 400). try: res = yield from http_utils.fetch('get', PVT_TOKEN_URL, cookies=self._cookies, connector=self._connector) CHAT_INIT_PARAMS['pvt'] = javascript.loads(res.body.decode())[1] logger.info('Found PVT token: {}'.format(CHAT_INIT_PARAMS['pvt'])) except (exceptions.NetworkError, ValueError) as e: raise exceptions.HangupsError( 'Failed to fetch PVT token: {}'.format(e)) # Now make the actual initialization request: try: res = yield from http_utils.fetch('get', CHAT_INIT_URL, cookies=self._cookies, params=CHAT_INIT_PARAMS, connector=self._connector) except exceptions.NetworkError as e: raise exceptions.HangupsError( 'Initialize chat request failed: {}'.format(e)) # Parse the response by using a regex to find all the JS objects, and # parsing them. Not everything will be parsable, but we don't care if # an object we don't need can't be parsed. data_dict = {} for data in CHAT_INIT_REGEX.findall(res.body.decode()): try: logger.debug("Attempting to load javascript: {}...".format( repr(data[:100]))) data = javascript.loads(data) # pylint: disable=invalid-sequence-index data_dict[data['key']] = data['data'] except ValueError as e: try: data = data.replace("data:function(){return", "data:") data = data.replace("}}", "}") data = javascript.loads(data) data_dict[data['key']] = data['data'] except ValueError as e: raise # logger.debug('Failed to parse initialize chat object: {}\n{}' # .format(e, data)) # Extract various values that we will need. try: self._api_key = data_dict['ds:7'][0][2] self._email = data_dict['ds:34'][0][2] self._header_date = data_dict['ds:2'][0][4] self._header_version = data_dict['ds:2'][0][6] self._header_id = data_dict['ds:4'][0][7] _sync_timestamp = parsers.from_timestamp( # cgserp? # data_dict['ds:21'][0][1][4] # data_dict['ds:35'][0][1][4] data_dict['ds:21'][0][1][4]) except KeyError as e: raise exceptions.HangupsError('Failed to get initialize chat ' 'value: {}'.format(e)) # Parse the entity representing the current user. self_entity = schemas.CLIENT_GET_SELF_INFO_RESPONSE.parse( # cgsirp? # data_dict['ds:20'][0] # data_dict['ds:35'][0] data_dict['ds:20'][0]).self_entity # Parse every existing conversation's state, including participants. initial_conv_states = schemas.CLIENT_CONVERSATION_STATE_LIST.parse( # csrcrp? # data_dict['ds:19'][0][3] # data_dict['ds:36'][0][3] data_dict['ds:19'][0][3]) initial_conv_parts = [] for conv_state in initial_conv_states: initial_conv_parts.extend(conv_state.conversation.participant_data) # Parse the entities for the user's contacts (doesn't include users not # in contacts). If this fails, continue without the rest of the # entities. initial_entities = [] try: entities = schemas.INITIAL_CLIENT_ENTITIES.parse( # cgserp? # data_dict['ds:21'][0] # data_dict['ds:37'][0] data_dict['ds:21'][0]) except ValueError as e: logger.warning( 'Failed to parse initial client entities: {}'.format(e)) else: initial_entities.extend(entities.entities) initial_entities.extend(e.entity for e in itertools.chain( entities.group1.entity, entities.group2.entity, entities. group3.entity, entities.group4.entity, entities.group5.entity)) return InitialData(initial_conv_states, self_entity, initial_entities, initial_conv_parts, _sync_timestamp)