Beispiel #1
0
    def get_1on1_conversation(self, chat_id):
        """find a 1-to-1 conversation with specified user
        maintained for functionality with older plugins that do not use get_1to1()
        """
        logger.warning('[DEPRECATED]: yield from bot.get_1to1(chat_id), instead of bot.get_1on1_conversation(chat_id)')

        if self.memory.exists(["user_data", chat_id, "optout"]):
            if self.memory.get_by_path(["user_data", chat_id, "optout"]):
                return False

        conversation = None

        if self.memory.exists(["user_data", chat_id, "1on1"]):
            conversation_id = self.memory.get_by_path(["user_data", chat_id, "1on1"])
            conversation = FakeConversation(self._client, conversation_id)
            logger.info(_("memory: {} is 1on1 with {}").format(conversation_id, chat_id))
        else:
            for c in self.list_conversations():
                if len(c.users) == 2:
                    for u in c.users:
                        if u.id_.chat_id == chat_id:
                            conversation = c
                            break

            if conversation is not None:
                # remember the conversation so we don't have to do this again
                self.initialise_memory(chat_id, "user_data")
                self.memory.set_by_path(["user_data", chat_id, "1on1"], conversation.id_)
                self.memory.save()

        return conversation
Beispiel #2
0
    def coro_send_message(self,
                          conversation,
                          message,
                          context=None,
                          image_id=None):
        if not message and not image_id:
            # at least a message OR an image_id must be supplied
            return

        # get the context

        if not context:
            context = {}

        if "base" not in context:
            # default legacy context
            context["base"] = self._messagecontext_legacy()

        # get the conversation id

        if isinstance(conversation,
                      (FakeConversation, hangups.conversation.Conversation)):
            conversation_id = conversation.id_
        elif isinstance(conversation, str):
            conversation_id = conversation
        else:
            raise ValueError('could not identify conversation id')

        # parse message strings to segments

        if message is None:
            segments = []
        elif "parser" in context and context["parser"] is False and isinstance(
                message, str):
            segments = [hangups.ChatMessageSegment(message)]
        elif isinstance(message, str):
            segments = simple_parse_to_segments(message)
        elif isinstance(message, list):
            segments = message
        else:
            raise TypeError("unknown message type supplied")

        # determine OTR status

        if "history" not in context:
            context["history"] = True
            try:
                context["history"] = self.conversations.catalog[
                    conversation_id]["history"]

            except KeyError:
                # rare scenario where a conversation was not refreshed
                # once the initial message goes through, convmem will be updated
                logger.warning(
                    "CORO_SEND_MESSAGE(): could not determine otr for {}".
                    format(conversation_id))

        if context["history"]:
            otr_status = OffTheRecordStatus.ON_THE_RECORD
        else:
            otr_status = OffTheRecordStatus.OFF_THE_RECORD

        broadcast_list = [(conversation_id, segments)]

        # run any sending handlers

        try:
            yield from self._handlers.run_pluggable_omnibus(
                "sending", self, broadcast_list, context)
        except self.Exceptions.SuppressEventHandling:
            logger.info("message sending: SuppressEventHandling")
            return
        except:
            raise

        logger.debug("message sending: global context = {}".format(context))

        # begin message sending.. for REAL!

        for response in broadcast_list:
            logger.debug("message sending: {}".format(response[0]))

            # send messages using FakeConversation as a workaround

            _fc = FakeConversation(self._client, response[0])

            try:
                yield from _fc.send_message(response[1],
                                            image_id=image_id,
                                            otr_status=otr_status)
            except hangups.NetworkError as e:
                logger.exception("CORO_SEND_MESSAGE: error sending {}".format(
                    response[0]))
Beispiel #3
0
    def get_1to1(self, chat_id):
        """find/create a 1-to-1 conversation with specified user
        config.autocreate-1to1 = false to revert to legacy behaviour of finding existing 1-to-1
        config.bot_introduction = "some text or html" to show to users when a new conversation
            is created - "{0}" will be substituted with first bot alias
        """

        if self.memory.exists(["user_data", chat_id, "optout"]):
            if self.memory.get_by_path(["user_data", chat_id, "optout"]):
                logger.info("get_1on1: user {} has optout".format(chat_id))
                return False

        conversation = None

        if self.memory.exists(["user_data", chat_id, "1on1"]):
            conversation_id = self.memory.get_by_path(
                ["user_data", chat_id, "1on1"])
            conversation = FakeConversation(self._client, conversation_id)
            logger.info("get_1on1: remembered {} for {}".format(
                conversation_id, chat_id))
        else:
            autocreate_1to1 = True if self.get_config_option(
                'autocreate-1to1') is not False else False
            if autocreate_1to1:
                """create a new 1-to-1 conversation with the designated chat id
                send an introduction message as well to the user as part of the chat creation
                """
                logger.info("get_1on1: creating 1to1 with {}".format(chat_id))
                try:
                    introduction = self.get_config_option('bot_introduction')
                    if not introduction:
                        introduction = _(
                            "<i>Hi there! I'll be using this channel to send private "
                            "messages and alerts. "
                            "For help, type <b>{0} help</b>. "
                            "To keep me quiet, reply with <b>{0} optout</b>.</i>"
                        ).format(self._handlers.bot_command[0])
                    response = yield from self._client.createconversation(
                        [chat_id])
                    new_conversation_id = response['conversation']['id']['id']
                    yield from self.coro_send_message(new_conversation_id,
                                                      introduction)
                    conversation = FakeConversation(self._client,
                                                    new_conversation_id)
                except Exception as e:
                    logger.exception(
                        "GET_1TO1: failed to create 1-to-1 for user {}".format(
                            chat_id))
            else:
                """legacy behaviour: user must say hi to the bot first
                this creates a conversation entry in self._conv_list (even if the bot receives
                a chat invite only - a message sent on the channel auto-accepts the invite)
                """
                logger.info(
                    "get_1on1: searching for existing 1to1 with {}".format(
                        chat_id))
                for c in self.list_conversations():
                    if len(c.users) == 2:
                        for u in c.users:
                            if u.id_.chat_id == chat_id:
                                conversation = c
                                break

            if conversation is not None:
                # remember the conversation so we don't have to do this again
                logger.info("get_1on1: determined {} for {}".format(
                    conversation.id_, chat_id))
                self.initialise_memory(chat_id, "user_data")
                self.memory.set_by_path(["user_data", chat_id, "1on1"],
                                        conversation.id_)
                self.memory.save()

        return conversation
    def coro_send_message(self,
                          conversation,
                          message,
                          context=None,
                          image_id=None):
        if not message and not image_id:
            # at least a message OR an image_id must be supplied
            return

        # get the context

        if not context:
            context = {}

        if "passthru" not in context:
            context['passthru'] = {}

        if "base" not in context:
            # default legacy context
            context["base"] = self._messagecontext_legacy()

        # get the conversation id

        if isinstance(conversation,
                      (FakeConversation, hangups.conversation.Conversation)):
            conversation_id = conversation.id_
        elif isinstance(conversation, str):
            conversation_id = conversation
        else:
            raise ValueError('could not identify conversation id')

        broadcast_list = [(conversation_id, message, image_id)]

        # run any sending handlers

        try:
            yield from self._handlers.run_pluggable_omnibus(
                "sending", self, broadcast_list, context)
        except self.Exceptions.SuppressEventHandling:
            logger.info("message sending: SuppressEventHandling")
            return
        except:
            raise

        logger.debug("message sending: global context = {}".format(context))

        # begin message sending.. for REAL!

        for response in broadcast_list:
            logger.debug("message sending: {}".format(response[0]))

            # send messages using FakeConversation as a workaround

            _fc = FakeConversation(self, response[0])

            try:
                yield from _fc.send_message(response[1],
                                            image_id=response[2],
                                            context=context)
            except hangups.NetworkError as e:
                logger.exception("CORO_SEND_MESSAGE: error sending {}".format(
                    response[0]))
    def get_1to1(self, chat_id, context=None):
        """find/create a 1-to-1 conversation with specified user
        config.autocreate-1to1 = false to revert to legacy behaviour of finding existing 1-to-1
        config.bot_introduction = "some text or html" to show to users when a new conversation
            is created - "{0}" will be substituted with first bot alias
        """

        if self.memory.exists(["user_data", chat_id, "optout"]):
            optout = self.memory.get_by_path(["user_data", chat_id, "optout"])
            if (isinstance(optout, list) and context
                    and 'initiator_convid' in context
                    and context['initiator_convid'] in optout):
                logger.info("get_1on1: user {} has optout for {}".format(
                    chat_id, context['initiator_convid']))
                return False
            elif isinstance(optout, bool) and optout:
                logger.info("get_1on1: user {} has optout".format(chat_id))
                return False

        if chat_id == self.user_self()["chat_id"]:
            logger.warning("1to1 conversations with myself are not supported",
                           stack_info=True)
            return False

        conversation = None

        if self.memory.exists(["user_data", chat_id, "1on1"]):
            conversation_id = self.memory.get_by_path(
                ["user_data", chat_id, "1on1"])
            conversation = FakeConversation(self, conversation_id)
            logger.info("get_1on1: remembered {} for {}".format(
                conversation_id, chat_id))
        else:
            autocreate_1to1 = True if self.get_config_option(
                'autocreate-1to1') is not False else False
            if autocreate_1to1:
                """create a new 1-to-1 conversation with the designated chat id
                send an introduction message as well to the user as part of the chat creation
                """
                logger.info("get_1on1: creating 1to1 with {}".format(chat_id))
                try:
                    introduction = self.get_config_option('bot_introduction')
                    if not introduction:
                        introduction = _(
                            "<i>Hi there! I'll be using this channel to send private "
                            "messages and alerts. "
                            "For help, type <b>{0} help</b>. "
                            "To keep me quiet, reply with <b>{0} optout</b>.</i>"
                        ).format(self._handlers.bot_command[0])

                    request = hangups.hangouts_pb2.CreateConversationRequest(
                        request_header=self._client.get_request_header(),
                        type=hangups.hangouts_pb2.CONVERSATION_TYPE_ONE_TO_ONE,
                        client_generated_id=self._client.
                        get_client_generated_id(),
                        invitee_id=[
                            hangups.hangouts_pb2.InviteeID(gaia_id=chat_id)
                        ])

                    response = yield from self._client.create_conversation(
                        request)

                    new_conversation_id = response.conversation.conversation_id.id

                    yield from self.coro_send_message(new_conversation_id,
                                                      introduction)
                    conversation = FakeConversation(self, new_conversation_id)
                except Exception as e:
                    logger.exception(
                        "GET_1TO1: failed to create 1-to-1 for user {}".format(
                            chat_id))
            else:
                """legacy behaviour: user must say hi to the bot first
                this creates a conversation entry in self._conv_list (even if the bot receives
                a chat invite only - a message sent on the channel auto-accepts the invite)
                """
                logger.info(
                    "get_1on1: searching for existing 1to1 with {}".format(
                        chat_id))
                for c in self.list_conversations():
                    if len(c.users) == 2:
                        for u in c.users:
                            if u.id_.chat_id == chat_id:
                                conversation = c
                                break

            if conversation is not None:
                # remember the conversation so we don't have to do this again
                logger.info("get_1on1: determined {} for {}".format(
                    conversation.id_, chat_id))
                self.initialise_memory(chat_id, "user_data")
                self.memory.set_by_path(["user_data", chat_id, "1on1"],
                                        conversation.id_)
                self.memory.save()

        return conversation