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
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]))
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