예제 #1
0
 def _get_event_request_header(self, conversation_id: str) -> hangouts.EventRequestHeader:
     return hangouts.EventRequestHeader(
         conversation_id=hangouts.ConversationId(
             id=conversation_id,
         ),
         client_generated_id=self.client.get_client_generated_id(),
     )
예제 #2
0
    def update_read_timestamp(self, read_timestamp=None):
        """Update the timestamp of the latest event which has been read.

        This method will avoid making an API request if it will have no effect.

        Args:
            read_timestamp (datetime.datetime): (optional) Timestamp to set.
                Defaults to the timestamp of the newest event.

        Raises:
            .NetworkError: If the timestamp cannot be updated.
        """
        if read_timestamp is None:
            read_timestamp = (self.events[-1].timestamp if self.events else
                              datetime.datetime.now(datetime.timezone.utc))
        if read_timestamp > self.latest_read_timestamp:
            logger.info(
                'Setting {} latest_read_timestamp from {} to {}'.format(
                    self.id_, self.latest_read_timestamp, read_timestamp))
            # Prevent duplicate requests by updating the conversation now.
            state = self._conversation.self_conversation_state
            state.self_read_state.latest_read_timestamp = (
                parsers.to_timestamp(read_timestamp))
            try:
                yield from self._client.update_watermark(
                    hangouts_pb2.UpdateWatermarkRequest(
                        request_header=self._client.get_request_header(),
                        conversation_id=hangouts_pb2.ConversationId(
                            id=self.id_),
                        last_read_timestamp=parsers.to_timestamp(
                            read_timestamp),
                    ))
            except exceptions.NetworkError as e:
                logger.warning('Failed to update read timestamp: {}'.format(e))
                raise
예제 #3
0
 async def set_typing(self, conversation_id: str, typing: bool) -> None:
     self.log.debug(f"set_typing({conversation_id}, {typing})")
     await self.client.set_typing(hangouts.SetTypingRequest(
         request_header=self.client.get_request_header(),
         conversation_id=hangouts.ConversationId(id=conversation_id),
         type=hangouts.TYPING_TYPE_STARTED if typing else hangouts.TYPING_TYPE_STOPPED,
     ))
예제 #4
0
    def leave(self):
        """Leave this conversation.

        Raises:
            .NetworkError: If conversation cannot be left.
        """
        is_group_conversation = (
            self._conversation.type == hangouts_pb2.CONVERSATION_TYPE_GROUP)
        try:
            if is_group_conversation:
                yield from self._client.remove_user(
                    hangouts_pb2.RemoveUserRequest(
                        request_header=self._client.get_request_header(),
                        event_request_header=self._get_event_request_header(),
                    ))
            else:
                yield from self._client.delete_conversation(
                    hangouts_pb2.DeleteConversationRequest(
                        request_header=self._client.get_request_header(),
                        conversation_id=hangouts_pb2.ConversationId(
                            id=self.id_),
                        delete_upper_bound_timestamp=parsers.to_timestamp(
                            datetime.datetime.now(tz=datetime.timezone.utc))))
        except exceptions.NetworkError as e:
            logger.warning('Failed to leave conversation: {}'.format(e))
            raise
예제 #5
0
    def _get_or_fetch_conversation(self, conv_id):
        """Get a cached conversation or fetch a missing conversation.

        Args:
            conv_id: string, conversation identifier

        Raises:
            NetworkError: If the request to fetch the conversation fails.

        Returns:
            :class:`.Conversation` with matching ID.
        """
        conv = self._conv_dict.get(conv_id, None)
        if conv is None:
            logger.info('Fetching unknown conversation %s', conv_id)
            res = yield from self._client.get_conversation(
                hangouts_pb2.GetConversationRequest(
                    request_header=self._client.get_request_header(),
                    conversation_spec=hangouts_pb2.ConversationSpec(
                        conversation_id=hangouts_pb2.ConversationId(
                            id=conv_id)),
                    include_event=False))
            return self._add_conversation(res.conversation_state.conversation)
        else:
            return conv
    def get_events(self, event_id=None, max_events=50):
        """Return list of ConversationEvents ordered newest-first.

        If event_id is specified, return events preceding this event.

        This method will make an API request to load historical events if
        necessary. If the beginning of the conversation is reached, an empty
        list will be returned.

        Raises KeyError if event_id does not correspond to a known event.

        Raises hangups.NetworkError if the events could not be requested.
        """
        if event_id is None:
            # If no event_id is provided, return the newest events in this
            # conversation.
            conv_events = self._events[-1 * max_events:]
        else:
            # If event_id is provided, return the events we have that are
            # older, or request older events if event_id corresponds to the
            # oldest event we have.
            conv_event = self.get_event(event_id)
            if self._events[0].id_ != event_id:
                conv_events = self._events[self._events.index(conv_event) + 1:]
            else:
                logger.info(
                    'Loading events for conversation {} before {}'.format(
                        self.id_, conv_event.timestamp))
                res = yield from self._client.get_conversation(
                    hangouts_pb2.GetConversationRequest(
                        request_header=self._client.get_request_header(),
                        conversation_spec=hangouts_pb2.ConversationSpec(
                            conversation_id=hangouts_pb2.ConversationId(
                                id=self.id_)),
                        include_event=True,
                        max_events_per_conversation=max_events,
                        event_continuation_token=(
                            hangouts_pb2.EventContinuationToken(
                                event_timestamp=parsers.to_timestamp(
                                    conv_event.timestamp)))))
                conv_events = [
                    self._wrap_event(event)
                    for event in res.conversation_state.event
                ]
                logger.info('Loaded {} events for conversation {}'.format(
                    len(conv_events), self.id_))
                # Iterate though the events newest to oldest.
                for conv_event in reversed(conv_events):
                    # Add event as the new oldest event, unless we already have
                    # it.
                    if conv_event.id_ not in self._events_dict:
                        self._events.insert(0, conv_event)
                        self._events_dict[conv_event.id_] = conv_event
                    else:
                        # If this happens, there's probably a bug.
                        logger.info(
                            'Conversation %s ignoring duplicate event %s',
                            self.id_, conv_event.id_)
        return conv_events
예제 #7
0
 def _get_event_request_header(self, conversation_id: str) -> hangouts.EventRequestHeader:
     delivery_medium = self.chats.get(conversation_id)._get_default_delivery_medium()
     return hangouts.EventRequestHeader(
         conversation_id=hangouts.ConversationId(
             id=conversation_id,
         ),
         delivery_medium=delivery_medium,
         client_generated_id=self.client.get_client_generated_id(),
     )
예제 #8
0
def easteregg_combo(client, conv_id, easteregg, eggcount=1, period=0.5):
    """Send easter egg combo (ponies, pitchforks, bikeshed, shydino)"""
    for i in range(eggcount):
        req = hangouts_pb2.EasterEggRequest(
            request_header=client.get_request_header(),
            conversation_id=hangouts_pb2.ConversationId(id=conv_id),
            easter_egg=hangouts_pb2.EasterEgg(message=easteregg))
        res = yield from client.easter_egg(req)
        if eggcount > 1:
            yield from asyncio.sleep(period + random.uniform(-0.1, 0.1))
예제 #9
0
    def sendchatmessage(
            self, conversation_id, segments, image_id=None,
            otr_status=hangouts_pb2.OFF_THE_RECORD_STATUS_ON_THE_RECORD,
            delivery_medium=None):
        """Send a chat message to a conversation.

        conversation_id must be a valid conversation ID. segments must be a
        list of message segments to send, in pblite format.

        otr_status determines whether the message will be saved in the server's
        chat history. Note that the OTR status of the conversation is
        irrelevant, clients may send messages with whatever OTR status they
        like.

        image_id is an option ID of an image retrieved from
        Client.upload_image. If provided, the image will be attached to the
        message.

        Raises hangups.NetworkError if the request fails.
        """
        segments_pb = []
        for segment_pblite in segments:
            segment_pb = hangouts_pb2.Segment()
            pblite.decode(segment_pb, segment_pblite)
            segments_pb.append(segment_pb)
        if delivery_medium is None:
            delivery_medium = hangouts_pb2.DeliveryMedium(
                medium_type=hangouts_pb2.DELIVERY_MEDIUM_BABEL,
            )

        request = hangouts_pb2.SendChatMessageRequest(
            request_header=self._get_request_header_pb(),
            message_content=hangouts_pb2.MessageContent(
                segment=segments_pb,
            ),
            event_request_header=hangouts_pb2.EventRequestHeader(
                conversation_id=hangouts_pb2.ConversationId(
                    id=conversation_id,
                ),
                client_generated_id=self.get_client_generated_id(),
                expected_otr=otr_status,
                delivery_medium=delivery_medium,
                event_type=hangouts_pb2.EVENT_TYPE_REGULAR_CHAT_MESSAGE,
            ),
        )

        if image_id is not None:
            request.existing_media = hangouts_pb2.ExistingMedia(
                photo=hangouts_pb2.Photo(photo_id=image_id)
            )

        response = hangouts_pb2.SendChatMessageResponse()
        yield from self._pb_request('conversations/sendchatmessage', request,
                                    response)
        return response
예제 #10
0
 def _get_event_request_header(self):
     """Return EventRequestHeader for conversation."""
     otr_status = (hangouts_pb2.OFF_THE_RECORD_STATUS_OFF_THE_RECORD
                   if self.is_off_the_record else
                   hangouts_pb2.OFF_THE_RECORD_STATUS_ON_THE_RECORD)
     return hangouts_pb2.EventRequestHeader(
         conversation_id=hangouts_pb2.ConversationId(id=self.id_),
         client_generated_id=self._client.get_client_generated_id(),
         expected_otr=otr_status,
         delivery_medium=self._get_default_delivery_medium(),
     )
예제 #11
0
 async def mark_read(self, conversation_id: str,
                     timestamp: Optional[Union[datetime.datetime, int]] = None) -> None:
     if isinstance(timestamp, datetime.datetime):
         timestamp = hangups.parsers.to_timestamp(timestamp)
     elif not timestamp:
         timestamp = int(time.time() * 1_000_000)
     await self.client.update_watermark(hangouts.UpdateWatermarkRequest(
         request_header=self.client.get_request_header(),
         conversation_id=hangouts.ConversationId(id=conversation_id),
         last_read_timestamp=timestamp,
     ))
예제 #12
0
 async def _get_event_request_header(self, conversation_id: str) -> hangouts.EventRequestHeader:
     if not self.chats:
         self.log.debug("Tried to send message before receiving chat list, waiting")
         await self.chats_future
     delivery_medium = self.chats.get(conversation_id)._get_default_delivery_medium()
     return hangouts.EventRequestHeader(
         conversation_id=hangouts.ConversationId(
             id=conversation_id,
         ),
         delivery_medium=delivery_medium,
         client_generated_id=self.client.get_client_generated_id(),
     )
예제 #13
0
 def on_message_sent(self, future, local_id=None):
     global loop, client
     try:
         pyotherside.send("remove-dummy-message", self.conv.id_, local_id)
         request = hangouts_pb2.SetFocusRequest(
             request_header=client.get_request_header(),
             conversation_id=hangouts_pb2.ConversationId(id=self.conv.id_),
             type=hangouts_pb2.FOCUS_TYPE_FOCUSED,
             timeout_secs=20,
         )
         asyncio.async(client.set_focus(request))
         print('Message sent successful')
     except hangups.NetworkError:
         print('Failed to send message')
    def set_notification_level(self, level):
        """Set the notification level of the conversation.

        Pass hangouts_pb2.NOTIFICATION_LEVEL_QUIET to disable notifications, or
        hangouts_pb2.NOTIFICATION_LEVEL_RING to enable them.

        Raises hangups.NetworkError if the request fails.
        """
        yield from self._client.set_conversation_notification_level(
            hangouts_pb2.SetConversationNotificationLevelRequest(
                request_header=self._client.get_request_header(),
                conversation_id=hangouts_pb2.ConversationId(id=self.id_),
                level=level,
            ))
예제 #15
0
    def updatewatermark(self, conv_id, read_timestamp):
        """Update the watermark (read timestamp) for a conversation.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.UpdateWatermarkRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conv_id),
            last_read_timestamp=parsers.to_timestamp(read_timestamp),
        )
        response = hangouts_pb2.UpdateWatermarkResponse()
        yield from self._pb_request('conversations/updatewatermark', request,
                                    response)
        return response
예제 #16
0
 async def _load_messages(self, source: 'u.User', limit: int = 100,
                          token: Optional[hangouts.EventContinuationToken] = None
                          ) -> Tuple[List[ChatMessageEvent], hangouts.EventContinuationToken]:
     resp = await source.client.get_conversation(hangouts.GetConversationRequest(
         request_header=source.client.get_request_header(),
         conversation_spec=hangouts.ConversationSpec(
             conversation_id=hangouts.ConversationId(id=self.gid),
         ),
         include_conversation_metadata=False,
         include_event=True,
         max_events_per_conversation=limit,
         event_continuation_token=token
     ))
     return ([HangoutsChat._wrap_event(evt) for evt in resp.conversation_state.event],
             resp.conversation_state.event_continuation_token)
예제 #17
0
    def setfocus(self, conversation_id):
        """Set focus to a conversation.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.SetFocusRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conversation_id),
            type=hangouts_pb2.FOCUS_TYPE_FOCUSED,
            timeout_secs=20,
        )
        response = hangouts_pb2.SetFocusResponse()
        yield from self._pb_request('conversations/setfocus', request,
                                    response)
        return response
예제 #18
0
    def on_entered(self):
        global client
        # Set the client as active.
        future = asyncio.async(client.set_active())
        future.add_done_callback(lambda future: future.result())

        future = asyncio.async(self.conv.update_read_timestamp())

        request = hangouts_pb2.SetFocusRequest(
            request_header=client.get_request_header(),
            conversation_id=hangouts_pb2.ConversationId(id=self.conv.id_),
            type=hangouts_pb2.FOCUS_TYPE_FOCUSED,
            timeout_secs=20,
        )
        asyncio.async(client.set_focus(request))
예제 #19
0
    def set_typing(self, typing):
        global client
        if typing == "typing":
            t = hangouts_pb2.TYPING_TYPE_STARTED
        elif typing == "paused":
            t = hangouts_pb2.TYPING_TYPE_PAUSED
        else:
            t = hangouts_pb2.TYPING_TYPE_STOPPED

        request = hangouts_pb2.SetTypingRequest(
            request_header=client.get_request_header(),
            conversation_id=hangouts_pb2.ConversationId(id=self.conv.id_),
            type=t,
        )

        asyncio.async(client.set_typing(request))
예제 #20
0
    async def set_notification_level(self, level):
        """Set the notification level of this conversation.

        Args:
            level: ``NOTIFICATION_LEVEL_QUIET`` to disable notifications, or
                ``NOTIFICATION_LEVEL_RING`` to enable them.

        Raises:
            .NetworkError: If the request fails.
        """
        await self._client.set_conversation_notification_level(
            hangouts_pb2.SetConversationNotificationLevelRequest(
                request_header=self._client.get_request_header(),
                conversation_id=hangouts_pb2.ConversationId(id=self.id_),
                level=level,
            ))
예제 #21
0
    def easteregg(self, conversation_id, easteregg):
        """Send an easteregg to a conversation.

        easteregg may not be empty.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.EasterEggRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conversation_id),
            easter_egg=hangouts_pb2.EasterEgg(message=easteregg),
        )
        response = hangouts_pb2.EasterEggResponse()
        yield from self._pb_request('conversations/easteregg', request,
                                    response)
        return response
예제 #22
0
    def add_users(self, users):
        global client

        request = hangouts_pb2.AddUserRequest(
            request_header=client.get_request_header(),
            invitee_id=[hangouts_pb2.InviteeID(gaia_id=chat_id)
                        for chat_id in users],
            event_request_header=hangouts_pb2.EventRequestHeader(
                conversation_id=hangouts_pb2.ConversationId(
                    id=self.conv.id_,
                ),
                client_generated_id=client.get_client_generated_id(),
                expected_otr=hangouts_pb2.OFF_THE_RECORD_STATUS_ON_THE_RECORD,
            ),
        )

        asyncio.async(client.add_user(request))
    def set_typing(self, typing=hangouts_pb2.TYPING_TYPE_STARTED):
        """Set typing status.

        TODO: Add rate-limiting to avoid unnecessary requests.

        Raises hangups.NetworkError if typing status cannot be set.
        """
        try:
            yield from self._client.set_typing(
                hangouts_pb2.SetTypingRequest(
                    request_header=self._client.get_request_header(),
                    conversation_id=hangouts_pb2.ConversationId(id=self.id_),
                    type=typing,
                ))
        except exceptions.NetworkError as e:
            logger.warning('Failed to set typing status: {}'.format(e))
            raise
예제 #24
0
파일: hangouts.py 프로젝트: Terrance/IMMP
 async def channel_link_create(self, channel, shared=True):
     try:
         conv = self._convs.get(channel.source)
     except KeyError:
         return None
     # Hangouts has no concept of private invite links.
     if not shared:
         return None
     # Enable joining via link for the conversation.
     if not conv.is_group_link_sharing_enabled:
         await conv.set_group_link_sharing_enabled(True)
         log.debug("Enabled join-by-link for %r", channel.source)
     # Request a new invite link (this won't revoke any existing ones).
     request = hangouts_pb2.GetGroupConversationUrlRequest(
         request_header=self._client.get_request_header(),
         conversation_id=hangouts_pb2.ConversationId(id=channel.source))
     response = await self._client.get_group_conversation_url(request)
     return response.group_conversation_url
예제 #25
0
    def settyping(self, conversation_id,
                  typing=hangouts_pb2.TYPING_TYPE_STARTED):
        """Send typing notification.

        conversation_id must be a valid conversation ID.
        typing must be a hangups.TypingType Enum.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.SetTypingRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conversation_id),
            type=typing,
        )
        response = hangouts_pb2.SetTypingResponse()
        yield from self._pb_request('conversations/settyping', request,
                                    response)
        return response
예제 #26
0
    def setconversationnotificationlevel(self, conversation_id, level):
        """Set the notification level of a conversation.

        Pass hangouts_pb2.NOTIFICATION_LEVEL_QUIET to disable notifications, or
        hangouts_pb2.NOTIFICATION_LEVEL_RING to enable them.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.SetConversationNotificationLevelRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conversation_id),
            level=level,
        )
        response = hangouts_pb2.SetConversationNotificationLevelResponse()
        yield from self._pb_request(
            'conversations/setconversationnotificationlevel', request, response
        )
        return response
예제 #27
0
    def set_typing(self, typing=hangouts_pb2.TYPING_TYPE_STARTED):
        """Set your typing status in this conversation.

        Args:
            typing: (optional) ``TYPING_TYPE_STARTED``, ``TYPING_TYPE_PAUSED``,
                or ``TYPING_TYPE_STOPPED`` to start, pause, or stop typing,
                respectively. Defaults to ``TYPING_TYPE_STARTED``.

        Raises:
            .NetworkError: If typing status cannot be set.
        """
        # TODO: Add rate-limiting to avoid unnecessary requests.
        try:
            yield from self._client.set_typing(
                hangouts_pb2.SetTypingRequest(
                    request_header=self._client.get_request_header(),
                    conversation_id=hangouts_pb2.ConversationId(id=self.id_),
                    type=typing,
                ))
        except exceptions.NetworkError as e:
            logger.warning('Failed to set typing status: {}'.format(e))
            raise
예제 #28
0
    def renameconversation(
            self, conversation_id, name,
            otr_status=hangouts_pb2.OFF_THE_RECORD_STATUS_ON_THE_RECORD):
        """Rename a conversation.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.RenameConversationRequest(
            request_header=self._get_request_header_pb(),
            new_name=name,
            event_request_header=hangouts_pb2.EventRequestHeader(
                conversation_id=hangouts_pb2.ConversationId(
                    id=conversation_id,
                ),
                client_generated_id=self.get_client_generated_id(),
                expected_otr=otr_status,
            ),
        )
        response = hangouts_pb2.RenameConversationResponse()
        yield from self._pb_request('conversations/renameconversation',
                                    request, response)
        return response
예제 #29
0
    def removeuser(
            self, conversation_id,
            otr_status=hangouts_pb2.OFF_THE_RECORD_STATUS_ON_THE_RECORD):
        """Leave group conversation.

        conversation_id must be a valid conversation ID.

        Raises hangups.NetworkError if the request fails.
        """
        request = hangouts_pb2.RemoveUserRequest(
            request_header=self._get_request_header_pb(),
            event_request_header=hangouts_pb2.EventRequestHeader(
                conversation_id=hangouts_pb2.ConversationId(
                    id=conversation_id,
                ),
                client_generated_id=self.get_client_generated_id(),
                expected_otr=otr_status,
            ),
        )
        response = hangouts_pb2.RemoveUserResponse()
        yield from self._pb_request('conversations/removeuser', request,
                                    response)
        return response
예제 #30
0
    def deleteconversation(self, conversation_id):
        """Delete one-to-one conversation.

        One-to-one conversations are "sticky"; they can't actually be deleted.
        This API clears the event history of the specified conversation up to
        delete_upper_bound_timestamp, hiding it if no events remain.

        conversation_id must be a valid conversation ID.

        Raises hangups.NetworkError if the request fails.
        """
        timestamp = parsers.to_timestamp(
            datetime.datetime.now(tz=datetime.timezone.utc)
        )
        request = hangouts_pb2.DeleteConversationRequest(
            request_header=self._get_request_header_pb(),
            conversation_id=hangouts_pb2.ConversationId(id=conversation_id),
            delete_upper_bound_timestamp=timestamp
        )
        response = hangouts_pb2.DeleteConversationResponse()
        yield from self._pb_request('conversations/deleteconversation',
                                    request, response)
        return response