def participant_ids(self): """Return the UserIDs involved in the membership change. Multiple users may be added to a conversation at the same time. """ return [user.UserID(chat_id=id_.chat_id, gaia_id=id_.gaia_id) for id_ in self._event.membership_change.participant_ids]
def users(self): """List of conversation participants (:class:`~hangups.user.User`).""" return [ self._user_list.get_user( user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id)) for part in self._conversation.participant_data ]
def users(self): """User instances of the conversation's current participants.""" return [ self._user_list.get_user( user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id)) for part in self._conversation.participant_data ]
def build_user_conversation_list(client): """Build :class:`.UserList` and :class:`.ConversationList`. This method requests data necessary to build the list of conversations and users. Users that are not in the contact list but are participating in a conversation will also be retrieved. Args: client (Client): Connected client. Returns: (:class:`.UserList`, :class:`.ConversationList`): Tuple of built objects. """ conv_states, sync_timestamp = yield from _sync_all_conversations(client) # Retrieve entities participating in all conversations. required_user_ids = set() for conv_state in conv_states: required_user_ids |= { user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id) for part in conv_state.conversation.participant_data } required_entities = [] if required_user_ids: logger.debug( 'Need to request additional users: {}'.format(required_user_ids)) try: response = yield from client.get_entity_by_id( hangouts_pb2.GetEntityByIdRequest( request_header=client.get_request_header(), batch_lookup_spec=[ hangouts_pb2.EntityLookupSpec( gaia_id=user_id.gaia_id, create_offnetwork_gaia=True, ) for user_id in required_user_ids ], )) for entity_result in response.entity_result: required_entities.extend(entity_result.entity) except exceptions.NetworkError as e: logger.warning('Failed to request missing users: {}'.format(e)) # Build list of conversation participants. conv_part_list = [] for conv_state in conv_states: conv_part_list.extend(conv_state.conversation.participant_data) # Retrieve self entity. get_self_info_response = yield from client.get_self_info( hangouts_pb2.GetSelfInfoRequest( request_header=client.get_request_header(), )) self_entity = get_self_info_response.self_entity user_list = user.UserList(client, self_entity, required_entities, conv_part_list) conversation_list = ConversationList(client, conv_states, user_list, sync_timestamp) return (user_list, conversation_list)
def parse_watermark_notification(client_watermark_notification): """Return WatermarkNotification from ClientWatermarkNotification.""" return WatermarkNotification( conv_id=client_watermark_notification.conversation_id.id_, user_id=user.UserID( chat_id=client_watermark_notification.participant_id.chat_id, gaia_id=client_watermark_notification.participant_id.gaia_id, ), read_timestamp=from_timestamp( client_watermark_notification.latest_read_timestamp), )
def parse_watermark_notification(p): """Return WatermarkNotification from hangouts_pb2.WatermarkNotification.""" return WatermarkNotification( conv_id=p.conversation_id.id, user_id=user.UserID( chat_id=p.sender_id.chat_id, gaia_id=p.sender_id.gaia_id, ), read_timestamp=from_timestamp( p.latest_read_timestamp ), )
def build_user_conversation_list(client): """Return UserList from initial contact data and an additional request. The initial data contains the user's contacts, but there may be conversions containing users that are not in the contacts. This function takes care of requesting data for those users and constructing the UserList. """ # Retrieve recent conversations so we can preemptively look up their # participants. sync_recent_conversations_response = (yield from client.syncrecentconversations()) conv_states = sync_recent_conversations_response.conversation_state sync_timestamp = parsers.from_timestamp( # syncrecentconversations seems to return a sync_timestamp 4 minutes # before the present. To prevent syncallnewevents later breaking # requesting events older than what we already have, use # current_server_time instead. sync_recent_conversations_response.response_header.current_server_time) # Retrieve entities participating in all conversations. required_user_ids = set() for conv_state in conv_states: required_user_ids |= { user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id) for part in conv_state.conversation.participant_data } required_entities = [] if required_user_ids: logger.debug( 'Need to request additional users: {}'.format(required_user_ids)) try: response = yield from client.getentitybyid( [user_id.gaia_id for user_id in required_user_ids]) required_entities = list(response.entity) except exceptions.NetworkError as e: logger.warning('Failed to request missing users: {}'.format(e)) # Build list of conversation participants. conv_part_list = [] for conv_state in conv_states: conv_part_list.extend(conv_state.conversation.participant_data) # Retrieve self entity. get_self_info_response = yield from client.getselfinfo() self_entity = get_self_info_response.self_entity user_list = user.UserList(client, self_entity, required_entities, conv_part_list) conversation_list = ConversationList(client, conv_states, user_list, sync_timestamp) return (user_list, conversation_list)
def parse_typing_status_message(p): """Return TypingStatusMessage from ClientSetTypingNotification. The same status may be sent multiple times consecutively, and when a message is sent the typing status will not change to stopped. """ return TypingStatusMessage( conv_id=p.conversation_id.id_, user_id=user.UserID(chat_id=p.user_id.chat_id, gaia_id=p.user_id.gaia_id), timestamp=from_timestamp(p.timestamp), status=p.status, )
async def add_users(self, *user_ids): """Add Users into this conversation. Args: user_ids (user.UserID): a tuple of `user.UserID`s. Raises: NetworkError: If a User cannot be added. """ present_users = set( user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id) for part in self._conversation.participant_data) new_user_ids = set(user_ids) - present_users if not new_user_ids: return await self._client.add_user( hangouts_pb2.AddUserRequest( request_header=self._client.get_request_header(), invitee_id=[ hangouts_pb2.InviteeID(gaia_id=user_id.chat_id) for user_id in new_user_ids ], event_request_header=self._get_event_request_header(), ))
def user_id(self): """A UserID indicating who created the event.""" return user.UserID(chat_id=self._event.sender_id.chat_id, gaia_id=self._event.sender_id.gaia_id)
def build_user_conversation_list(client): """Return UserList from initial contact data and an additional request. The initial data contains the user's contacts, but there may be conversions containing users that are not in the contacts. This function takes care of requesting data for those users and constructing the UserList. """ # Retrieve recent conversations so we can preemptively look up their # participants. sync_recent_conversations_response = ( yield from client.sync_recent_conversations( hangouts_pb2.SyncRecentConversationsRequest( request_header=client.get_request_header(), max_conversations=100, max_events_per_conversation=1, sync_filter=[hangouts_pb2.SYNC_FILTER_INBOX], ))) conv_states = sync_recent_conversations_response.conversation_state sync_timestamp = parsers.from_timestamp( # syncrecentconversations seems to return a sync_timestamp 4 minutes # before the present. To prevent syncallnewevents later breaking # requesting events older than what we already have, use # current_server_time instead. sync_recent_conversations_response.response_header.current_server_time) # Retrieve entities participating in all conversations. required_user_ids = set() for conv_state in conv_states: required_user_ids |= { user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id) for part in conv_state.conversation.participant_data } required_entities = [] if required_user_ids: logger.debug( 'Need to request additional users: {}'.format(required_user_ids)) try: response = yield from client.get_entity_by_id( hangouts_pb2.GetEntityByIdRequest( request_header=client.get_request_header(), batch_lookup_spec=[ hangouts_pb2.EntityLookupSpec( gaia_id=user_id.gaia_id, create_offnetwork_gaia=True, ) for user_id in required_user_ids ], )) for entity_result in response.entity_result: required_entities.extend(entity_result.entity) except exceptions.NetworkError as e: logger.warning('Failed to request missing users: {}'.format(e)) # Build list of conversation participants. conv_part_list = [] for conv_state in conv_states: conv_part_list.extend(conv_state.conversation.participant_data) # Retrieve self entity. get_self_info_response = yield from client.get_self_info( hangouts_pb2.GetSelfInfoRequest( request_header=client.get_request_header(), )) self_entity = get_self_info_response.self_entity user_list = user.UserList(client, self_entity, required_entities, conv_part_list) conversation_list = ConversationList(client, conv_states, user_list, sync_timestamp) return (user_list, conversation_list)
def from_participantid(participant_id): """Convert hangouts_pb2.ParticipantId to UserID.""" return user.UserID(chat_id=participant_id.chat_id, gaia_id=participant_id.gaia_id)
def user_id(self): """Who created the event (:class:`~hangups.user.UserID`).""" return user.UserID(chat_id=self._event.sender_id.chat_id, gaia_id=self._event.sender_id.gaia_id)
def participant_ids(self): """:class:`~hangups.user.UserID` of users involved (:class:`list`).""" return [user.UserID(chat_id=id_.chat_id, gaia_id=id_.gaia_id) for id_ in self._event.membership_change.participant_ids]
def build_user_conversation_list(client): """Build :class:`~UserList` and :class:`~ConversationList`. This method requests data necessary to build the list of conversations and users. Users that are not in the contact list but are participating in a conversation will also be retrieved. Args: client (Client): Connected client. Returns: (:class:`~UserList`, :class:`~ConversationList`): Tuple of built objects. """ # Retrieve conversations in groups of CONVERSATIONS_PER_REQUEST. conv_states = [] sync_timestamp, next_timestamp = None, None last_synced = CONVERSATIONS_PER_REQUEST while last_synced == CONVERSATIONS_PER_REQUEST: response = ( yield from client.sync_recent_conversations( hangouts_pb2.SyncRecentConversationsRequest( request_header=client.get_request_header(), last_event_timestamp=next_timestamp, max_conversations=CONVERSATIONS_PER_REQUEST, max_events_per_conversation=1, sync_filter=[ hangouts_pb2.SYNC_FILTER_INBOX, hangouts_pb2.SYNC_FILTER_ARCHIVED, ] ) ) ) # Add these conversations to the list of states response_conv_states = response.conversation_state min_timestamp = float('inf') for conv_state in response_conv_states: conv_event = conv_state.event[0] if conv_event.timestamp < min_timestamp: min_timestamp = conv_event.timestamp conv_states.append(conv_state) # Update the number of conversations synced and sync timestamp last_synced = len(response_conv_states) sync_timestamp = parsers.from_timestamp( # SyncRecentConversations seems to return a sync_timestamp 4 # minutes before the present. To prevent SyncAllNewEvents later # breaking requesting events older than what we already have, use # current_server_time instead. response.response_header.current_server_time ) logger.debug('Added {} conversations'.format(last_synced)) if math.isfinite(min_timestamp): # Start syncing conversations just before this one next_timestamp = min_timestamp - 1 else: # No minimum timestamp; abort. next_timestamp = 0 break logger.info('Synced {} total conversations'.format(len(conv_states))) # Retrieve entities participating in all conversations. required_user_ids = set() for conv_state in conv_states: required_user_ids |= { user.UserID(chat_id=part.id.chat_id, gaia_id=part.id.gaia_id) for part in conv_state.conversation.participant_data } required_entities = [] if required_user_ids: logger.debug('Need to request additional users: {}' .format(required_user_ids)) try: response = yield from client.get_entity_by_id( hangouts_pb2.GetEntityByIdRequest( request_header=client.get_request_header(), batch_lookup_spec=[ hangouts_pb2.EntityLookupSpec( gaia_id=user_id.gaia_id, create_offnetwork_gaia=True, ) for user_id in required_user_ids ], ) ) for entity_result in response.entity_result: required_entities.extend(entity_result.entity) except exceptions.NetworkError as e: logger.warning('Failed to request missing users: {}'.format(e)) # Build list of conversation participants. conv_part_list = [] for conv_state in conv_states: conv_part_list.extend(conv_state.conversation.participant_data) # Retrieve self entity. get_self_info_response = yield from client.get_self_info( hangouts_pb2.GetSelfInfoRequest( request_header=client.get_request_header(), ) ) self_entity = get_self_info_response.self_entity user_list = user.UserList(client, self_entity, required_entities, conv_part_list) conversation_list = ConversationList(client, conv_states, user_list, sync_timestamp) return (user_list, conversation_list)