def convecho(bot, event, *args): """echo back text into filtered conversations""" posix_args = get_posix_args(args) if(len(posix_args) > 1): if not posix_args[0]: """block spamming ALL conversations""" text = _("<em>sending to ALL conversations not allowed</em>") convlist = bot.conversations.get(filter=event.conv_id) else: convlist = bot.conversations.get(filter=posix_args[0]) text = ' '.join(posix_args[1:]) test_segments = simple_parse_to_segments(text) if test_segments: if test_segments[0].text.lower().strip().startswith(tuple([cmd.lower() for cmd in bot._handlers.bot_command])): """detect and reject attempts to exploit botalias""" text = _("<em>command echo blocked</em>") convlist = bot.conversations.get(filter=event.conv_id) elif len(posix_args) == 1 and posix_args[0].startswith("id:"): """specialised error message for /devilbot echo (implied convid: <event.conv_id>)""" text = _("<em>missing text</em>") convlist = bot.conversations.get(filter=event.conv_id) else: """general error""" text = _("<em>required parameters: convfilter text</em>") convlist = bot.conversations.get(filter=event.conv_id) if not convlist: text = _("<em>no conversations filtered</em>") convlist = bot.conversations.get(filter=event.conv_id) for convid, convdata in convlist.items(): yield from bot.coro_send_message(convid, text)
def send_data(self, conversation_id, html, image_data=None, image_filename=None): """sends html and/or image to a conversation image_filename is recommended but optional, fallbacks to <timestamp>.jpg if undefined process_request() should determine the image extension prior to this """ image_id = None if image_data: if not image_filename: image_filename = str(int(time.time())) + ".jpg" logging.warning( "fallback image filename: {}".format(image_filename)) image_id = yield from self._bot._client.upload_image( image_data, filename=image_filename) if not html and not image_id: print("{}: nothing to send".format(self.sinkname)) return segments = simple_parse_to_segments(html) print("{}: sending segments: {}".format(self.sinkname, len(segments))) self._bot.send_message_segments(conversation_id, segments, context=None, image_id=image_id)
def convecho(bot, event, *args): """echo back text into filtered conversations""" posix_args = get_posix_args(args) if(len(posix_args) > 1): if not posix_args[0]: """block spamming ALL conversations""" text = _("<em>sending to ALL conversations not allowed</em>") convlist = bot.conversations.get(filter=event.conv_id) else: convlist = bot.conversations.get(filter=posix_args[0]) text = ' '.join(posix_args[1:]) test_segments = simple_parse_to_segments(text) if test_segments: if test_segments[0].text.lower().strip().startswith(tuple([cmd.lower() for cmd in bot._handlers.bot_command])): """detect and reject attempts to exploit botalias""" text = _("<em>command echo blocked</em>") convlist = bot.conversations.get(filter=event.conv_id) elif len(posix_args) == 1 and posix_args[0].startswith("id:"): """specialised error message for /bot echo (implied convid: <event.conv_id>)""" text = _("<em>missing text</em>") convlist = bot.conversations.get(filter=event.conv_id) else: """general error""" text = _("<em>required parameters: convfilter text</em>") convlist = bot.conversations.get(filter=event.conv_id) if not convlist: text = _("<em>no conversations filtered</em>") convlist = bot.conversations.get(filter=event.conv_id) for convid, convdata in convlist.items(): bot.send_message_parsed(convid, text)
def process_payload(self, path, query_string, payload): sinkname = self.sinkname path = path.split("/") conversation_id = path[1] if conversation_id is None: print("{}: conversation id must be provided as part of path".format(sinkname)) return image_id = None if "image" in payload: image_data = False image_filename = False if "base64encoded" in payload["image"]: raw = base64.b64decode(payload["image"]["base64encoded"]) image_data = io.BytesIO(raw) if "filename" in payload["image"]: image_filename = payload["image"]["filename"] else: image_filename = str(int(time.time())) + ".jpg" print("{}: uploading image: {}".format(sinkname, image_filename)) image_id = yield from webhookReceiver._bot._client.upload_image(image_data, filename=image_filename) html = "" if "echo" in payload: html = payload["echo"] else: # placeholder text html = "<b>hello world</b>" segments = simple_parse_to_segments(html) print("{} sending segments: {}".format(sinkname, len(segments))) webhookReceiver._bot.send_message_segments(conversation_id, segments, context=None, image_id=image_id)
def echoparsed(bot, event, *args): """echo back requested text""" formatted_text = ' '.join(args) test_segments = simple_parse_to_segments(formatted_text) if test_segments: if test_segments[0].text.strip().startswith(tuple([cmd.lower() for cmd in bot._handlers.bot_command])): formatted_text = _("NOPE! Some things aren't worth repeating.") bot.send_message_parsed(event.conv, formatted_text)
def send_message_parsed(self, conversation, html, context=None, image_id=None): logger.debug( '[DEPRECATED]: yield from bot.coro_send_message()' ' instead of send_message_parsed()') segments = simple_parse_to_segments(html) asyncio.async( self.coro_send_message( conversation, segments, context=context, image_id=image_id ) ).add_done_callback(lambda future: future.result())
def send_message_parsed(self, conversation, html, context=None, image_id=None): logger.debug('[DEPRECATED]: yield from bot.coro_send_message()' ' instead of send_message_parsed()') segments = simple_parse_to_segments(html) asyncio.async( self.coro_send_message(conversation, segments, context=context, image_id=image_id) ).add_done_callback(lambda future: future.result())
def send_to_bridged_1to1(self, user_id, bridge_id, message): if bridge_id is None or bridge_id in self.conversations.catalog: # Bridge ID is in fact a hangout, defer to existing handling. conv = yield from self.get_1to1(user_id) yield from self.coro_send_message(conv.id_, message) elif bridge_id in self.bridges: # Defer to the bridge to handle sending this message. bridge = self.bridges[bridge_id] # Strip formatting -- we don't know what format the external side is expecting. # Turn it into segments and just keep the plain text. segments = simple_parse_to_segments(message) message = "".join(segment.text for segment in segments) yield from bridge.send_to_external_1to1(user_id, message)
async def process_payload(self, path, query_string, payload): logging.warning( "[DEPRECATED] simpledemo.webhookReceiver, use sinks.generic.SimpleMessagePoster" ) sinkname = self.sinkname path = path.split("/") conversation_id = path[1] if conversation_id is None: print( "{}: conversation id must be provided as part of path".format( sinkname)) return image_id = None if "image" in payload: image_data = False image_filename = False image_type = 'unknown' if "base64encoded" in payload["image"]: raw = base64.b64decode(payload["image"]["base64encoded"], None, True) image_data = io.BytesIO(raw) image_type = imghdr.what('ignore', raw) if not image_type: image_type = 'error' if "filename" in payload["image"]: image_filename = payload["image"]["filename"] else: image_filename = str(int(time.time())) + "." + image_type print("{}: uploading image: {}".format(sinkname, image_filename)) image_id = await webhookReceiver._bot._client.upload_image( image_data, filename=image_filename) html = "" if "echo" in payload: html = payload["echo"] else: # placeholder text html = "<b>hello world</b>" segments = simple_parse_to_segments(html) print("{} sending segments: {}".format(sinkname, len(segments))) await self._bot.coro_send_message(conversation_id, segments, context=None, image_id=image_id)
def send_data(self, conversation_id, html, image_data=None, image_filename=None): """sends html and/or image to a conversation image_filename is optional, defaults to <timestamp>.jpg if not defined """ image_id = None if image_data: if not image_filename: image_filename = str(int(time.time())) + ".jpg" image_id = yield from self._bot._client.upload_image(image_data, filename=image_filename) if not html and not image_id: print("{}: nothing to send".format(self.sinkname)) return segments = simple_parse_to_segments(html) print("{}: sending segments: {}".format(self.sinkname, len(segments))) self._bot.send_message_segments(conversation_id, segments, context=None, image_id=image_id)
def echoparsed(bot, event, *args): """echo back requested text""" # Check if the first argument is a known conv_id match if args[0] in list(bot.memory.get_by_path(["conv_data"]).keys()): formatted_text = ' '.join(args[1:]) conv_id = args[0] else: formatted_text = ' '.join(args) conv_id = event.conv_id test_segments = simple_parse_to_segments(formatted_text) if test_segments: if test_segments[0].text.strip().startswith(tuple([cmd.lower() for cmd in bot._handlers.bot_command])): formatted_text = _("NOPE! Some things aren't worth repeating.") conv_id = event.conv_id bot.send_message_parsed(conv_id, formatted_text)
def process_payload(self, path, query_string, payload): logging.warning( "[DEPRECATED] simpledemo.webhookReceiver, use sinks.generic.SimpleMessagePoster") sinkname = self.sinkname path = path.split("/") conversation_id = path[1] if conversation_id is None: print("{}: conversation id must be provided as part of path".format(sinkname)) return image_id = None if "image" in payload: image_data = False image_filename = False image_type = 'unknown' if "base64encoded" in payload["image"]: raw = base64.b64decode( payload["image"]["base64encoded"], None, True) image_data = io.BytesIO(raw) image_type = imghdr.what('ignore', raw) if not image_type: image_type = 'error' if "filename" in payload["image"]: image_filename = payload["image"]["filename"] else: image_filename = str(int(time.time())) + "." + image_type print("{}: uploading image: {}".format(sinkname, image_filename)) image_id = yield from webhookReceiver._bot._client.upload_image(image_data, filename=image_filename) html = "" if "echo" in payload: html = payload["echo"] else: # placeholder text html = "<b>hello world</b>" segments = simple_parse_to_segments(html) print("{} sending segments: {}".format(sinkname, len(segments))) yield from self._bot.coro_send_message(conversation_id, segments, context=None, image_id=image_id)
def send_data(self, conversation_id, html, image_data=None, image_filename=None): """sends html and/or image to a conversation image_filename is recommended but optional, fallbacks to <timestamp>.jpg if undefined process_request() should determine the image extension prior to this """ image_id = None if image_data: if not image_filename: image_filename = str(int(time.time())) + ".jpg" logging.warning("fallback image filename: {}".format(image_filename)) image_id = yield from self._bot._client.upload_image(image_data, filename=image_filename) if not html and not image_id: print("{}: nothing to send".format(self.sinkname)) return segments = simple_parse_to_segments(html) print("{}: sending segments: {}".format(self.sinkname, len(segments))) self._bot.send_message_segments(conversation_id, segments, context=None, image_id=image_id)
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): message = re.sub('f**k', 'fsck', message, flags=re.I) message = re.sub('n***a|nigger', 'n***a', message, flags=re.I) message = re.sub('bitch', 'b***h', message, flags=re.I) message = re.sub('w***e', 'w***e', message, flags=re.I) segments = [hangups.ChatMessageSegment(message)] elif isinstance(message, str): message = re.sub('f**k', 'fsck', message, flags=re.I) message = re.sub('n***a|nigger', 'n***a', message, flags=re.I) message = re.sub('bitch', 'b***h', message, flags=re.I) message = re.sub('w***e', 'w***e', message, flags=re.I) segments = simple_parse_to_segments(message) elif isinstance(message, list): message = [re.sub('f**k', 'fsck', seg, flags=re.I) for seg in message] message = [re.sub('n***a|nigger', 'n***a', seg, flags=re.I) for seg in message] message = [re.sub('bitch', 'b***h', seg, flags=re.I) for seg in message] message = [re.sub('w***e', 'w***e', seg, flags=re.I) for seg in message] 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 send_message(self, message, image_id=None, otr_status=None, context=None): """ChatMessageSegment: parse message""" if message is None: # nothing to do if the message is blank segments = [] raw_message = "" elif "parser" in context and context["parser"] is False and isinstance(message, str): # no parsing requested, escape anything in raw_message that can be construed as valid markdown segments = [hangups.ChatMessageSegment(message)] raw_message = message.replace("*", "\\*").replace("_", "\\_").replace("`", "\\`") elif isinstance(message, str): # preferred method: markdown-formatted message (or less preferable but OK: html) segments = simple_parse_to_segments(message) raw_message = message elif isinstance(message, list): # who does this anymore? logger.error( "[INVALID]: send messages as html or markdown, " "not as list of ChatMessageSegment, context={}".format(context) ) segments = message raw_message = "".join([ segment_to_html(seg) for seg in message ]) else: raise TypeError("unknown message type supplied") if segments: serialised_segments = [seg.serialize() for seg in segments] else: serialised_segments = None if "original_request" not in context["passthru"]: context["passthru"]["original_request"] = { "message": raw_message, "image_id": image_id, "segments": segments } """OffTheRecordStatus: determine history""" if otr_status is None: if "history" not in context: context["history"] = True try: context["history"] = self.bot.conversations.catalog[self.id_]["history"] except KeyError: # rare scenario where a conversation was not refreshed # once the initial message goes through, convmem will be updated logger.warning("could not determine otr for {}".format(self.id_)) if context["history"]: otr_status = hangups_shim.schemas.OffTheRecordStatus.ON_THE_RECORD else: otr_status = hangups_shim.schemas.OffTheRecordStatus.OFF_THE_RECORD """ExistingMedia: attach previously uploaded media for display""" media_attachment = None if image_id: media_attachment = hangups.hangouts_pb2.ExistingMedia( photo = hangups.hangouts_pb2.Photo( photo_id = image_id )) """EventAnnotation: combine with client-side storage to allow custom messaging context""" annotations = [] if "reprocessor" in context: annotations.append( hangups.hangouts_pb2.EventAnnotation( type = 1025, value = context["reprocessor"]["id"] )) # define explicit "passthru" in context to "send" any type of variable if "passthru" in context: annotations.append( hangups.hangouts_pb2.EventAnnotation( type = 1026, value = self.bot._handlers.register_passthru(context["passthru"]) )) # always implicitly "send" the entire context dictionary annotations.append( hangups.hangouts_pb2.EventAnnotation( type = 1027, value = self.bot._handlers.register_context(context) )) """send the message""" with (yield from asyncio.Lock()): yield from self._client.send_chat_message( hangups.hangouts_pb2.SendChatMessageRequest( request_header = self._client.get_request_header(), message_content = hangups.hangouts_pb2.MessageContent( segment=serialised_segments ), existing_media = media_attachment, annotation = annotations, event_request_header = hangups.hangouts_pb2.EventRequestHeader( conversation_id=hangups.hangouts_pb2.ConversationId( id=self.id_ ), client_generated_id=self._client.get_client_generated_id(), expected_otr = otr_status )))
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 send_message_parsed(self, conversation, html): segments = simple_parse_to_segments(html) self.send_message_segments(conversation, segments)
def send_message(self, message, image_id=None, otr_status=None, context=None): """ChatMessageSegment: parse message""" if message is None: # nothing to do if the message is blank segments = [] raw_message = "" elif "parser" in context and context["parser"] is False and isinstance( message, str): # no parsing requested, escape anything in raw_message that can be construed as valid markdown segments = [hangups.ChatMessageSegment(message)] raw_message = message.replace("*", "\\*").replace("_", "\\_").replace( "`", "\\`") elif isinstance(message, str): # preferred method: markdown-formatted message (or less preferable but OK: html) segments = simple_parse_to_segments(message) raw_message = message elif isinstance(message, list): # who does this anymore? logger.error( "[INVALID]: send messages as html or markdown, " "not as list of ChatMessageSegment, context={}".format( context)) segments = message raw_message = "".join([segment_to_html(seg) for seg in message]) else: raise TypeError("unknown message type supplied") if segments: serialised_segments = [seg.serialize() for seg in segments] else: serialised_segments = None if "original_request" not in context["passthru"]: context["passthru"]["original_request"] = { "message": raw_message, "image_id": image_id, "segments": segments } """OffTheRecordStatus: determine history""" if otr_status is None: if "history" not in context: context["history"] = True try: context["history"] = self.bot.conversations.catalog[ self.id_]["history"] except KeyError: # rare scenario where a conversation was not refreshed # once the initial message goes through, convmem will be updated logger.warning("could not determine otr for {}".format( self.id_)) if context["history"]: otr_status = hangups_shim.schemas.OffTheRecordStatus.ON_THE_RECORD else: otr_status = hangups_shim.schemas.OffTheRecordStatus.OFF_THE_RECORD """ExistingMedia: attach previously uploaded media for display""" media_attachment = None if image_id: media_attachment = hangups.hangouts_pb2.ExistingMedia( photo=hangups.hangouts_pb2.Photo(photo_id=image_id)) """EventAnnotation: combine with client-side storage to allow custom messaging context""" annotations = [] if "reprocessor" in context: annotations.append( hangups.hangouts_pb2.EventAnnotation( type=1025, value=context["reprocessor"]["id"])) # define explicit "passthru" in context to "send" any type of variable if "passthru" in context: annotations.append( hangups.hangouts_pb2.EventAnnotation( type=1026, value=self.bot._handlers.register_passthru( context["passthru"]))) # always implicitly "send" the entire context dictionary annotations.append( hangups.hangouts_pb2.EventAnnotation( type=1027, value=self.bot._handlers.register_context(context))) """ Assuming working with phones """ default_medium = hangups.hangouts_pb2.DeliveryMedium( medium_type=hangups.hangouts_pb2.DELIVERY_MEDIUM_GOOGLE_VOICE) """send the message""" with (yield from asyncio.Lock()): yield from self._client.send_chat_message( hangups.hangouts_pb2.SendChatMessageRequest( request_header=self._client.get_request_header(), message_content=hangups.hangouts_pb2.MessageContent( segment=serialised_segments), existing_media=media_attachment, annotation=annotations, event_request_header=hangups.hangouts_pb2. EventRequestHeader( conversation_id=hangups.hangouts_pb2.ConversationId( id=self.id_), client_generated_id=self._client. get_client_generated_id(), expected_otr=otr_status, delivery_medium=default_medium)))
def send_message_parsed(self, conversation, html, context=None): segments = simple_parse_to_segments(html) self.send_message_segments(conversation, segments, context)