def attach_media(self, msg: Message, attachment: Dict[str, Any]): """ Attach an attachment to a message. Args: msg: Message to be attached to attachment: Dict of information of the attachment ``fbchat`` entity is not used as it is not completed. """ self.logger.debug("[%s] Trying to attach media: %s", msg.uid, attachment) blob_attachment: Dict[str, Any] = attachment.get('mercury', {}).get('blob_attachment', {}) attachment_type: str = blob_attachment.get("__typename", None) if 'sticker_attachment' in attachment.get('mercury', {}): attachment_type = '__Sticker' if 'extensible_attachment' in attachment.get('mercury', {}): attachment_type = '__Link' extensible_type = get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment', 'target', '__typename')) if extensible_type == 'MessageLocation': attachment_type = 'MessageLocation' elif extensible_type == 'MessageLiveLocation': attachment_type = 'MessageLocation' # TODO: Change if live location is supported by framework. self.logger.debug("[%s] The attachment has type %s", msg.uid, attachment_type) msg.filename = attachment.get('filename', None) msg.mime = attachment.get('mimeType', None) if attachment_type == "MessageAudio": msg.type = MsgType.Voice msg.filename = msg.filename or 'audio.mp3' msg.mime = msg.mime or 'audio/mpeg' ext = os.path.splitext(msg.filename)[1] msg.file = NamedTemporaryFile(suffix=ext) msg.file.write(requests.get(blob_attachment['playable_url']).content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == 'MessageImage': msg.type = MsgType.Image msg.filename = msg.filename or 'image.png' msg.mime = msg.mime or 'image/png' attribution_app = get_value(blob_attachment, ('attribution_app', 'name')) if attribution_app: if msg.text: msg.text += " (via %s)" % attribution_app else: msg.text = "via %s" % attribution_app ext = os.path.splitext(msg.filename)[1] msg.file = NamedTemporaryFile(suffix=ext) url = self.fetchImageUrl(attachment['id']) msg.file.write(requests.get(url).content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == 'MessageAnimatedImage': msg.type = MsgType.Animation msg.filename = msg.filename or 'image.gif' msg.mime = msg.mime or 'image/gif' ext = os.path.splitext(msg.filename)[1] msg.file = NamedTemporaryFile(suffix=ext) msg.file.write(requests.get(blob_attachment['animated_image']['uri'], allow_redirects=True).content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == 'MessageFile': msg.type = MsgType.File msg.filename = msg.filename or 'file' msg.mime = msg.mime or 'application/octet-stream' ext = os.path.splitext(msg.filename)[1] msg.file = NamedTemporaryFile(suffix=ext) msg.file.write(requests.get(self.process_url(blob_attachment['url'], True), allow_redirects=True).content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == 'MessageVideo': msg.type = MsgType.Image msg.filename = msg.filename or 'video.mp4' msg.mime = msg.mime or 'video/mpeg' ext = os.path.splitext(msg.filename)[1] msg.file = NamedTemporaryFile(suffix=ext) msg.file.write(requests.get(blob_attachment['playable_url'], allow_redirects=True).content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == '__Sticker': if get_value(attachment, ('mercury', 'sticker_attachment', 'pack', 'id')) == "227877430692340": self.logger.debug("[%s] Sticker received is a \"Like\" sticker. Converting message to text.", msg.uid) sticker_id = get_value(attachment, ('mercury', 'sticker_attachment', 'id')) for i in EmojiSize: if sticker_id == i.value: msg.type = MsgType.Text msg.text = "👍 (%s)" % i.name[0] return msg.type = MsgType.Sticker url = attachment['mercury']['sticker_attachment']['url'] msg.text = attachment['mercury']['sticker_attachment'].get('label', '') filename = os.path.split(urllib.parse.urlparse(url).path)[1] msg.filename = filename ext = os.path.splitext(filename)[1] response = requests.get(url) msg.mime = response.headers['content-type'] msg.file = NamedTemporaryFile(suffix=ext) msg.file.write(response.content) msg.file.seek(0) msg.path = Path(msg.file.name) elif attachment_type == '__Link': msg.type = MsgType.Link link_information = get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment'), {}) title = get_value(link_information, ('title_with_entities', 'text'), '') description = get_value(link_information, ('description', 'text'), '') source = get_value(link_information, ('source', 'text'), None) if source: description += " (via %s)" % source preview = get_value(link_information, ('media', 'playable_url'), None) if \ get_value(link_information, ('media', 'is_playable'), False) else None preview = preview or get_value(link_information, ('media', 'image', 'uri'), None) url = link_information.get('url', preview) msg.attributes = LinkAttribute(title=title, description=description, image=self.process_url(preview), url=self.process_url(url)) elif attachment_type == 'MessageLocation': msg.type = MsgType.Location link_information = get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment'), {}) title = get_value(link_information, ('title_with_entities', 'text'), '') description = get_value(link_information, ('description', 'text'), '') msg.text = '\n'.join([title, description]) preview = get_value(link_information, ('media', 'image', 'uri'), None) matches = re.search(r'markers=([\d.-]+)%2C([\d.-]+)', preview) if matches: latitude, longitude = map(float, matches.groups()) else: msg.type = MsgType.Unsupported msg.text = self._("Message type unsupported.\n{content}").format(msg.text) return msg.attributes = LocationAttribute( latitude=latitude, longitude=longitude ) else: msg.type = MsgType.Unsupported msg.text = self._("Message type unsupported.\n{content}").format(msg.text)
def send_message(self, msg: EFBMessage) -> Message: self.logger.debug("Received message from master: %s", msg) try: target_msg_offset = 0 prefix = "" mentions = [] # Send message reaction # if msg.target and msg.text.startswith('r`') and \ # msg.target.uid.startswith("mid.$"): # self.logger.debug("[%s] Message is a reaction to another message: %s", msg.uid, msg.text) # msg_id = ".".join(msg.target.uid.split(".", 2)[:2]) # if getattr(MessageReaction, msg.text[2:], None): # self.client.reactToMessage(msg_id, getattr(MessageReaction, msg.text[2:])) # else: # self.client.reactToMessage(msg_id, self.CustomReaction(msg.text[2:])) # msg.uid = "__reaction__" # return msg # Message substitutions if msg.substitutions: self.logger.debug("[%s] Message has substitutions: %s", msg.uid, msg.substitutions) for i in msg.substitutions: mentions.append(Mention(msg.substitutions[i].id, target_msg_offset + i[0], i[1] - i[0])) self.logger.debug("[%s] Translated to mentions: %s", msg.uid, mentions) fb_msg = Message(text=prefix + msg.text, mentions=mentions) thread: Thread = self.client.fetchThreadInfo(msg.chat.uid)[str(msg.chat.uid)] if msg.target and msg.target.uid: fb_msg.reply_to_id = msg.target.uid if msg.type in (MsgType.Text, MsgType.Unsupported): # Remove variation selector-16 (force colored emoji) for # matching. emoji_compare = msg.text.replace("\uFE0F", "") if emoji_compare == "👍": fb_msg.sticker = Sticker(uid=EmojiSize.SMALL.value) if not prefix: fb_msg.text = None elif emoji_compare[:-1] == "👍" and emoji_compare[-1] in 'SML': if emoji_compare[-1] == 'S': fb_msg.sticker = Sticker(uid=EmojiSize.SMALL.value) elif emoji_compare[-1] == 'M': fb_msg.sticker = Sticker(uid=EmojiSize.MEDIUM.value) elif emoji_compare[-1] == 'L': fb_msg.sticker = Sticker(uid=EmojiSize.LARGE.value) if not prefix: fb_msg.text = None elif emoji_compare[:-1] in emoji.UNICODE_EMOJI and emoji_compare[-1] in 'SML': # type: ignore self.logger.debug("[%s] Message is an Emoji message: %s", msg.uid, emoji_compare) if emoji_compare[-1] == 'S': fb_msg.emoji_size = EmojiSize.SMALL elif emoji_compare[-1] == 'M': fb_msg.emoji_size = EmojiSize.MEDIUM elif emoji_compare[-1] == 'L': fb_msg.emoji_size = EmojiSize.LARGE fb_msg.text = emoji_compare[:-1] msg.uid = self.client.send(fb_msg, thread_id=thread.uid, thread_type=thread.type) elif msg.type in (MsgType.Image, MsgType.Sticker, MsgType.Animation): msg_uid = self.client.send_image_file(msg.filename, msg.file, msg.mime, message=fb_msg, thread_id=thread.uid, thread_type=thread.type) if msg_uid.startswith('mid.$'): self.client.sent_messages.add(msg_uid) self.logger.debug("Sent message with ID %s", msg_uid) msg.uid = msg_uid elif msg.type == MsgType.Voice: files = self.upload_file(msg, voice_clip=True) msg_uid = self.client._sendFiles(files=files, message=fb_msg, thread_id=thread.uid, thread_type=thread.type) if msg_uid.startswith('mid.$'): self.client.sent_messages.add(msg_uid) self.logger.debug("Sent message with ID %s", msg_uid) msg.uid = msg_uid elif msg.type in (MsgType.File, MsgType.Video): files = self.upload_file(msg) msg_uid = self.client._sendFiles(files=files, message=fb_msg, thread_id=thread.uid, thread_type=thread.type) if msg_uid.startswith('mid.$'): self.client.sent_messages.add(msg_uid) self.logger.debug("Sent message with ID %s", msg_uid) msg.uid = msg_uid elif msg.type == MsgType.Status: assert (isinstance(msg.attributes, StatusAttribute)) status: StatusAttribute = msg.attributes if status.status_type in (StatusAttribute.Types.TYPING, StatusAttribute.Types.UPLOADING_VOICE, StatusAttribute.Types.UPLOADING_VIDEO, StatusAttribute.Types.UPLOADING_IMAGE, StatusAttribute.Types.UPLOADING_FILE): self.client.setTypingStatus(TypingStatus.TYPING, thread_id=thread.uid, thread_type=thread.type) threading.Timer(status.timeout / 1000, self.stop_typing, args=(thread.uid, thread.type)).start() elif msg.type == MsgType.Link: assert (isinstance(msg.attributes, LinkAttribute)) link: LinkAttribute = msg.attributes if self.flag('send_link_with_description'): info: Tuple[str, ...] = (link.title,) if link.description: info += (link.description,) info += (link.url,) text = "\n".join(info) else: text = link.url if fb_msg.text: text = fb_msg.text + "\n" + text fb_msg.text = text msg.uid = self.client.send(fb_msg, thread_id=thread.uid, thread_type=thread.type) elif msg.type == MsgType.Location: assert (isinstance(msg.attributes, LocationAttribute)) location_attr: LocationAttribute = msg.attributes location = LocationAttachment(latitude=location_attr.latitude, longitude=location_attr.longitude) self.client.sendPinnedLocation(location, fb_msg, thread_id=thread.uid, thread_type=thread.type) else: raise EFBMessageTypeNotSupported() return msg finally: if msg.file and not msg.file.closed: msg.file.close() self.client.markAsSeen() self.client.markAsRead(msg.chat.uid)
def attach_msg_type(self, msg: Message, attachment: Dict[str, Any]): """ Attach message_type to a message. Args: msg: Message to be attached to attachment: Dict of information of the attachment ``fbchat`` entity is not used as it is not completed. """ blob_attachment: Dict[str, Any] = attachment.get('mercury', {}).get('blob_attachment', attachment) attachment_type: str = blob_attachment.get("__typename", None) if 'sticker_attachment' in attachment.get('mercury', {}): attachment_type = '__Sticker' if 'extensible_attachment' in attachment.get('mercury', {}): attachment_type = '__Link' if get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment', 'target', '__typename')) == 'MessageLocation': attachment_type = 'MessageLocation' if attachment_type == "MessageAudio": msg.type = MsgType.Voice elif attachment_type == 'MessageImage': msg.type = MsgType.Image elif attachment_type == 'MessageAnimatedImage': msg.type = MsgType.Image elif attachment_type == 'MessageFile': msg.type = MsgType.File elif attachment_type == 'MessageVideo': msg.type = MsgType.Image elif attachment_type == '__Sticker': msg.type = MsgType.Sticker msg.text = attachment['mercury']['sticker_attachment'].get('label', '') elif attachment_type == '__Link': msg.type = MsgType.Link link_information = get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment'), {}) title = get_value(link_information, ('title_with_entities', 'text'), '') description = get_value(link_information, ('description', 'text'), '') source = get_value(link_information, ('source', 'text'), None) if source: description += " (via %s)" % source preview = get_value(link_information, ('media', 'playable_url'), None) if \ get_value(link_information, ('media', 'is_playable'), False) else None preview = preview or get_value(link_information, ('media', 'image', 'uri'), None) url = link_information['media'].get('url', preview) msg.attributes = LinkAttribute(title=title, description=description, image=self.process_url(preview), url=self.process_url(url)) elif attachment_type == 'MessageLocation': msg.type = MsgType.Location link_information = get_value(attachment, ('mercury', 'extensible_attachment', 'story_attachment'), {}) title = get_value(link_information, ('title_with_entities', 'text'), '') description = get_value(link_information, ('description', 'text'), '') msg.text = '\n'.join([title, description]) preview = get_value(link_information, ('media', 'image', 'uri'), None) matches = re.search(r'markers=([\d.-]+)%2C([\d.-]+)', preview) if matches: latitude, longitude = map(float, matches.groups()) else: msg.type = MsgType.Unsupported msg.text = self._("Message type unsupported.\n{content}").format(msg.text) return msg.attributes = LocationAttribute( latitude=latitude, longitude=longitude ) else: msg.type = MsgType.Unsupported msg.text = self._("Message type unsupported.\n{content}").format(msg.text)