예제 #1
0
    async def on_connect_later(self) -> None:
        try:
            info = await self.client.get_self_info(
                hangouts.GetSelfInfoRequest(
                    request_header=self.client.get_request_header()))
        except Exception:
            self.log.exception("Failed to get_self_info")
            return
        self.gid = info.self_entity.id.gaia_id
        self.name = info.self_entity.properties.display_name
        self.name_future.set_result(self.name)
        self.save()

        try:
            puppet = pu.Puppet.get_by_gid(self.gid)
            if puppet.custom_mxid != self.mxid and puppet.can_auto_login(
                    self.mxid):
                self.log.info(f"Automatically enabling custom puppet")
                await puppet.switch_mxid(access_token="auto", mxid=self.mxid)
        except Exception:
            self.log.exception("Failed to automatically enable custom puppet")

        try:
            await self.sync()
        except Exception:
            self.log.exception("Failed to sync conversations and users")
예제 #2
0
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)
예제 #3
0
    def set_active(self):
        """Set this client as active.

        While a client is active, no other clients will raise notifications.
        Call this method whenever there is an indication the user is
        interacting with this client. This method may be called very
        frequently, and it will only make a request when necessary.
        """
        is_active = (self._active_client_state ==
                     hangouts_pb2.ACTIVE_CLIENT_STATE_IS_ACTIVE)
        timed_out = (time.time() - self._last_active_secs >
                     SETACTIVECLIENT_LIMIT_SECS)
        if not is_active or timed_out:
            # Update these immediately so if the function is called again
            # before the API request finishes, we don't start extra requests.
            self._active_client_state = (
                hangouts_pb2.ACTIVE_CLIENT_STATE_IS_ACTIVE
            )
            self._last_active_secs = time.time()

            # The first time this is called, we need to retrieve the user's
            # email address.
            if self._email is None:
                try:
                    get_self_info_request = hangouts_pb2.GetSelfInfoRequest(
                        request_header=self.get_request_header(),
                    )
                    get_self_info_response = yield from self.get_self_info(
                        get_self_info_request
                    )
                except exceptions.NetworkError as e:
                    logger.warning('Failed to find email address: {}'
                                   .format(e))
                    return
                self._email = (
                    get_self_info_response.self_entity.properties.email[0]
                )

            # If the client_id hasn't been received yet, we can't set the
            # active client.
            if self._client_id is None:
                logger.info(
                    'Cannot set active client until client_id is received'
                )
                return

            try:
                set_active_request = hangouts_pb2.SetActiveClientRequest(
                    request_header=self.get_request_header(),
                    is_active=True,
                    full_jid="{}/{}".format(self._email, self._client_id),
                    timeout_secs=ACTIVE_TIMEOUT_SECS,
                )
                yield from self.set_active_client(set_active_request)
            except exceptions.NetworkError as e:
                logger.warning('Failed to set active client: {}'.format(e))
            else:
                logger.info('Set active client for {} seconds'
                            .format(ACTIVE_TIMEOUT_SECS))
예제 #4
0
 async def get_self(self):
     # Retrieve self entity.
     get_self_info_response = await self.client.get_self_info(
         hangouts_pb2.GetSelfInfoRequest(
             request_header=self.client.get_request_header(),
         )
     )
     user = get_self_info_response.self_entity
     return hangups.user.User.from_entity(user, None)
예제 #5
0
    def getselfinfo(self):
        """Return information about your account.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.GetSelfInfoRequest(
            request_header=self._get_request_header_pb(),
        )
        response = hangouts_pb2.GetSelfInfoResponse()
        yield from self._pb_request('contacts/getselfinfo', request, response)
        return response
예제 #6
0
파일: hangouts.py 프로젝트: raveinid/IMMP
 async def _connect(self):
     log.debug("Retrieving users and conversations")
     self._users, self._convs = await hangups.build_user_conversation_list(
         self._client)
     self._convs.on_event.add_observer(self._event)
     resp = await self._client.get_self_info(
         hangouts_pb2.GetSelfInfoRequest(
             request_header=self._client.get_request_header()))
     self._bot_user = resp.self_entity.id.chat_id
     async with self._starting:
         self._starting.notify_all()
예제 #7
0
async def ping(evt: CommandEvent) -> None:
    try:
        info = await evt.sender.client.get_self_info(
            hangouts.GetSelfInfoRequest(
                request_header=evt.sender.client.get_request_header()))
    except Exception as e:
        evt.log.exception("Failed to get user info", exc_info=True)
        await evt.reply(f"Failed to get user info: {e}")
        return
    name = info.self_entity.properties.display_name
    email = (f" <{info.self_entity.properties.email[0]}>"
             if info.self_entity.properties.email else "")
    id = info.self_entity.id.gaia_id
    await evt.reply(f"You're logged in as {name}{email} ({id})",
                    allow_html=False)
예제 #8
0
 async def on_connect_later(self) -> None:
     try:
         info = await self.client.get_self_info(hangouts.GetSelfInfoRequest(
             request_header=self.client.get_request_header()
         ))
     except Exception:
         self.log.exception("Failed to get_self_info")
         return
     self.gid = info.self_entity.id.gaia_id
     self.name = info.self_entity.properties.display_name
     self.name_future.set_result(self.name)
     self.save()
     try:
         await self.sync()
     except Exception:
         self.log.exception("Failed to sync conversations and users")
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)
예제 #10
0
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)