def wechat_shared_image_msg(self, msg: wxpy.Message, source: str, text: str = "", mode: str = "image") -> Message: efb_msg = Message() efb_msg.type = MsgType.Image efb_msg.text = self._("Via {source}").format(source=source) if text: efb_msg.text = "%s\n%s" % (text, efb_msg.text) efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg, app_message=mode) return efb_msg
def wechat_voice_msg(self, msg: wxpy.Message) -> Message: efb_msg = Message(type=MsgType.Voice) try: efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg) efb_msg.text = "" except EOFError: efb_msg.type = MsgType.Text efb_msg.text += self._("[Failed to download the voice message, please check your phone.]") return efb_msg
def qq_file_after_wrapper(self, data): efb_msg = Message() efb_msg.file = data['file'] efb_msg.type = MsgType.File mime = magic.from_file(efb_msg.file.name, mime=True) if isinstance(mime, bytes): mime = mime.decode() efb_msg.path = efb_msg.file.name efb_msg.mime = mime efb_msg.filename = quote(data['filename']) return efb_msg
def wechat_video_msg(self, msg: wxpy.Message) -> Message: efb_msg = Message(type=MsgType.Video) try: if msg.file_size == 0: raise EOFError efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg) efb_msg.filename = msg.file_name efb_msg.text = "" except EOFError: efb_msg.type = MsgType.Text efb_msg.text += self._("[Failed to download the video message, please check your phone.]") return efb_msg
def wechat_file_msg(self, msg: wxpy.Message) -> Message: efb_msg = Message(type=MsgType.File) try: file_name = msg.file_name efb_msg.text = file_name or "" app_name = msg.app_name if app_name: efb_msg.text = self._("{file_name} sent via {app_name}").format(file_name=file_name, app_name=app_name) efb_msg.filename = file_name or "" efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg) except EOFError: efb_msg.type = MsgType.Text efb_msg.text += self._("[Failed to download the file, please check your phone.]") return efb_msg
def wechat_picture_msg(self, msg: wxpy.Message) -> Message: efb_msg = Message(type=MsgType.Image) try: if msg.raw['MsgType'] == 47 and not msg.raw['Content']: raise EOFError if msg.file_size == 0: raise EOFError efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg) efb_msg.filename = msg.file_name # ^ Also throws EOFError efb_msg.text = "" except EOFError: efb_msg.text += self._("[Failed to download the picture, please check your phone.]") efb_msg.type = MsgType.Unsupported return efb_msg
def qq_record_wrapper(self, data, chat: Chat = None): # Experimental! efb_msg = Message() try: transformed_file = self.inst.coolq_api_query("get_record", file=data['file'], out_format='mp3') efb_msg.type = MsgType.Audio efb_msg.file = download_voice(transformed_file['file'], self.inst.client_config['api_root'].rstrip("/"), self.inst.client_config['access_token']) mime = magic.from_file(efb_msg.file.name, mime=True) if isinstance(mime, bytes): mime = mime.decode() efb_msg.path = efb_msg.file.name efb_msg.mime = mime except Exception: efb_msg.type = MsgType.Unsupported efb_msg.text = self._('[Voice Message] Please check it on your QQ') logging.getLogger(__name__).exception("Failed to download voice") return [efb_msg]
def wechat_sticker_msg(self, msg: wxpy.Message) -> Message: efb_msg = Message(type=MsgType.Sticker) try: if msg.raw['MsgType'] == 47 and not msg.raw['Content']: raise EOFError if msg.file_size == 0: raise EOFError efb_msg.path, efb_msg.mime, efb_msg.file = self.save_file(msg) efb_msg.filename = msg.file_name # ^ Also throws EOFError if 'gif' in efb_msg.mime and Image.open(efb_msg.path).is_animated: efb_msg.type = MsgType.Animation efb_msg.text = "" except EOFError: efb_msg.text += self._("[Failed to download the sticker, please check your phone.]") efb_msg.type = MsgType.Unsupported return efb_msg
def edit_file_like_message(self, message: Message, file_path: Path, mime: str, reactions: bool = False, commands: bool = False, substitution: bool = False) -> Message: message.text = f"Content of edited {message.type.name} media with ID {message.uid}" message.edit = True message.edit_media = True message.file = file_path.open('rb') message.filename = file_path.name message.path = file_path message.mime = mime message = self.attach_message_properties(message, reactions, commands, substitution) self.messages_sent[message.uid] = message coordinator.send_message(message) return message
def master_qr_code(self, uuid, status, qrcode=None): status = int(status) if self.qr_uuid == (uuid, status): return self.qr_uuid = (uuid, status) msg = Message( uid=f"ews_auth_{uuid}_{status}_{uuid4()}", type=MsgType.Text, chat=self.user_auth_chat, author=self.user_auth_chat.other, deliver_to=coordinator.master, ) if status == 201: msg.type = MsgType.Text msg.text = self._('Confirm on your phone.') self.master_qr_picture_id = None elif status == 200: msg.type = MsgType.Text msg.text = self._("Successfully logged in.") self.master_qr_picture_id = None elif uuid != self.qr_uuid: msg.type = MsgType.Image file = NamedTemporaryFile(suffix=".png") qr_url = "https://login.weixin.qq.com/l/" + uuid QRCode(qr_url).png(file, scale=10) msg.text = self._("QR code expired, please scan the new one.") msg.path = Path(file.name) msg.file = file msg.mime = 'image/png' if self.master_qr_picture_id is not None: msg.edit = True msg.edit_media = True msg.uid = self.master_qr_picture_id else: self.master_qr_picture_id = msg.uid if status in (200, 201) or uuid != self.qr_uuid: coordinator.send_message(msg)
def reply_message_img(self, message: Message, im3: Image.Image): reply = Message() # reply.text = text # reply.chat = coordinator.slaves[message.chat.channel_id].get_chat(message.chat.chat_uid) reply.chat = coordinator.slaves[message.chat.module_id].get_chat( message.chat.uid) reply.author = message.chat.make_system_member( uid=self.middleware_id, name=self.middleware_name, middleware=self) reply.type = MsgType.Image reply.mime = 'image/png' f = tempfile.NamedTemporaryFile(suffix='.png') img_data = io.BytesIO() im3.save(img_data, format='png') f.write(img_data.getvalue()) f.file.seek(0) reply.file = f reply.path = f.name reply.filename = os.path.basename(reply.file.name) # reply.deliver_to = coordinator.master reply.deliver_to = coordinator.slaves[message.chat.module_id] # reply.target = message reply.uid = str(uuid.uuid4()) r2 = reply coordinator.send_message(reply) r2.type = MsgType.Image r2.mime = 'image/png' f = tempfile.NamedTemporaryFile(suffix='.png') img_data = io.BytesIO() im3.save(img_data, format='png') f.write(img_data.getvalue()) f.file.seek(0) r2.file = f r2.path = f.name r2.filename = os.path.basename(r2.file.name) r2.deliver_to = coordinator.master coordinator.send_message(r2)
def qq_image_wrapper(self, data, chat: Chat = None): efb_msg = Message() if 'url' not in data: efb_msg.type = MsgType.Text efb_msg.text = self._('[Image Source missing]') return [efb_msg] efb_msg.file = cq_get_image(data['url']) if efb_msg.file is None: efb_msg.type = MsgType.Text efb_msg.text = self._('[Download image failed, please check on your QQ client]') return [efb_msg] efb_msg.type = MsgType.Image mime = magic.from_file(efb_msg.file.name, mime=True) if isinstance(mime, bytes): mime = mime.decode() efb_msg.filename = data['file'] if 'file' in data else efb_msg.file.name efb_msg.filename += '.' + str(mime).split('/')[1] efb_msg.path = efb_msg.file.name efb_msg.mime = mime if "gif" in mime: efb_msg.type = MsgType.Animation return [efb_msg]
def send_message(self, msg: Message) -> Message: """Send a message to WeChat. Supports text, image, sticker, and file. Args: msg (channel.Message): Message Object to be sent. Returns: This method returns nothing. Raises: EFBChatNotFound: Raised when a chat required is not found. EFBMessageTypeNotSupported: Raised when the message type sent is not supported by the channel. EFBOperationNotSupported: Raised when an message edit request is sent, but not supported by the channel. EFBMessageNotFound: Raised when an existing message indicated is not found. E.g.: The message to be edited, the message referred in the :attr:`msg.target <.Message.target>` attribute. EFBMessageError: Raised when other error occurred while sending or editing the message. """ if msg.chat == self.user_auth_chat: raise EFBChatNotFound chat: wxpy.Chat = self.chats.get_wxpy_chat_by_uid(msg.chat.uid) # List of "SentMessage" response for all messages sent r: List[wxpy.SentMessage] = [] self.logger.info("[%s] Sending message to WeChat:\n" "uid: %s\n" "UserName: %s\n" "NickName: %s\n" "Type: %s\n" "Text: %s", msg.uid, msg.chat.uid, chat.user_name, chat.name, msg.type, msg.text) try: chat.mark_as_read() except wxpy.ResponseError as e: self.logger.exception( "[%s] Error occurred while marking chat as read. (%s)", msg.uid, e) send_text_only = False self.logger.debug('[%s] Is edited: %s', msg.uid, msg.edit) if msg.edit and msg.uid: if self.flag('delete_on_edit'): msg_ids = json.loads(msg.uid) if msg.type in self.MEDIA_MSG_TYPES and not msg.edit_media: # Treat message as text message to prevent resend of media msg_ids = msg_ids[1:] send_text_only = True failed = 0 for i in msg_ids: try: ews_utils.message_id_to_dummy_message(i, self).recall() except wxpy.ResponseError as e: self.logger.error( "[%s] Trying to recall message but failed: %s", msg.uid, e) failed += 1 if failed: raise EFBMessageError( self.ngettext('Failed to recall {failed} out of {total} message, edited message was not sent.', 'Failed to recall {failed} out of {total} messages, edited message was not sent.', len(msg_ids)).format( failed=failed, total=len(msg_ids) )) # Not caching message ID as message recall feedback is not needed in edit mode else: raise EFBOperationNotSupported() if send_text_only or msg.type in [MsgType.Text, MsgType.Link]: if isinstance(msg.target, Message): max_length = self.flag("max_quote_length") qt_txt = msg.target.text or msg.target.type.name if max_length > 0: if len(qt_txt) >= max_length: tgt_text = qt_txt[:max_length] tgt_text += "…" else: tgt_text = qt_txt elif max_length < 0: tgt_text = qt_txt else: tgt_text = "" if isinstance(chat, wxpy.Group) and not isinstance(msg.target.author, SelfChatMember): tgt_alias = "@%s\u2005:" % msg.target.author.display_name else: tgt_alias = "" msg.text = f"「{tgt_alias}{tgt_text}」\n- - - - - - - - - - - - - - -\n{msg.text}" r.append(self._bot_send_msg(chat, msg.text)) self.logger.debug( '[%s] Sent as a text message. %s', msg.uid, msg.text) elif msg.type in (MsgType.Image, MsgType.Sticker, MsgType.Animation): self.logger.info("[%s] Image/GIF/Sticker %s", msg.uid, msg.type) convert_to = None file = msg.file assert file is not None if self.flag('send_stickers_and_gif_as_jpeg'): if msg.type == MsgType.Sticker or msg.mime == "image/gif": convert_to = "image/jpeg" else: if msg.type == MsgType.Sticker: convert_to = "image/gif" if convert_to == "image/gif": with NamedTemporaryFile(suffix=".gif") as f: try: img = Image.open(file) try: alpha = img.split()[3] mask = Image.eval( alpha, lambda a: 255 if a <= 128 else 0) except IndexError: mask = Image.eval(img.split()[0], lambda a: 0) img = img.convert('RGB').convert( 'P', palette=Image.ADAPTIVE, colors=255) img.paste(255, mask) img.save(f, transparency=255) msg.path = Path(f.name) self.logger.debug( '[%s] Image converted from %s to GIF', msg.uid, msg.mime) file.close() if f.seek(0, 2) > self.MAX_FILE_SIZE: raise EFBMessageError( self._("Image size is too large. (IS02)")) f.seek(0) r.append(self._bot_send_image(chat, f.name, f)) finally: if not file.closed: file.close() elif convert_to == "image/jpeg": with NamedTemporaryFile(suffix=".jpg") as f: try: img = Image.open(file).convert('RGBA') out = Image.new("RGBA", img.size, (255, 255, 255, 255)) out.paste(img, img) out.convert('RGB').save(f) msg.path = Path(f.name) self.logger.debug( '[%s] Image converted from %s to JPEG', msg.uid, msg.mime) file.close() if f.seek(0, 2) > self.MAX_FILE_SIZE: raise EFBMessageError( self._("Image size is too large. (IS02)")) f.seek(0) r.append(self._bot_send_image(chat, f.name, f)) finally: if not file.closed: file.close() else: try: if file.seek(0, 2) > self.MAX_FILE_SIZE: raise EFBMessageError( self._("Image size is too large. (IS01)")) file.seek(0) self.logger.debug( "[%s] Sending %s (image) to WeChat.", msg.uid, msg.path) filename = msg.filename or (msg.path and msg.path.name) assert filename r.append(self._bot_send_image(chat, filename, file)) finally: if not file.closed: file.close() if msg.text: r.append(self._bot_send_msg(chat, msg.text)) elif msg.type in (MsgType.File, MsgType.Audio): self.logger.info("[%s] Sending %s to WeChat\nFileName: %s\nPath: %s\nFilename: %s", msg.uid, msg.type, msg.text, msg.path, msg.filename) filename = msg.filename or (msg.path and msg.path.name) assert filename and msg.file r.append(self._bot_send_file(chat, filename, file=msg.file)) if msg.text: self._bot_send_msg(chat, msg.text) if not msg.file.closed: msg.file.close() elif msg.type == MsgType.Video: self.logger.info( "[%s] Sending video to WeChat\nFileName: %s\nPath: %s", msg.uid, msg.text, msg.path) filename = msg.filename or (msg.path and msg.path.name) assert filename and msg.file r.append(self._bot_send_video(chat, filename, file=msg.file)) if msg.text: r.append(self._bot_send_msg(chat, msg.text)) if not msg.file.closed: msg.file.close() else: raise EFBMessageTypeNotSupported() msg.uid = ews_utils.generate_message_uid(r) self.logger.debug( 'WeChat message is assigned with unique ID: %s', msg.uid) return msg
def handle_tg_img_preview(self, message: Message): if not message or not message.file or not message.filename: return if message.author.uid == self.middleware_id: # trysh-middleware # self.lg('self') return if message.type != MsgType.Image: return try: message.file.seek(0) fbs = message.file.read() message.file.seek(0) im: Image.Image = Image.open(io.BytesIO(fbs)) max_size = max(im.size) min_size = min(im.size) img_ratio = max_size / min_size if img_ratio < 10.0: return im2 = im.copy() for _ in range(100): max_size = max(im.size) min_size = min(im.size) img_ratio = max_size / min_size if img_ratio >= 10.0: if im.width == min_size: im = im.resize((im.width * 2, im.height), box=(0, 0, 1, 1)) else: im = im.resize((im.width, im.height * 2), box=(0, 0, 1, 1)) continue else: break im.paste(im2, (0, 0, im2.width, im2.height)) im3 = im.convert('RGB') # im.copy() # reply = Message() # reply.text = text # reply.chat = coordinator.slaves[message.chat.channel_id].get_chat(message.chat.chat_uid) reply.chat = coordinator.slaves[message.chat.module_id].get_chat( message.chat.uid) reply.author = message.chat.make_system_member( uid=self.middleware_id, name=self.middleware_name, middleware=self) reply.type = MsgType.Image reply.mime = 'image/png' f = tempfile.NamedTemporaryFile(suffix='.png') img_data = io.BytesIO() im3.save(img_data, format='png') f.write(img_data.getvalue()) f.file.seek(0) reply.file = f reply.path = f.name reply.filename = os.path.basename(reply.file.name) # reply.deliver_to = coordinator.master reply.deliver_to = coordinator.master # reply.target = message reply.uid = str(uuid.uuid4()) coordinator.send_message(reply) except BaseException as e: self.lg(f'handle_tg_img_preview e:{e}') pass
def process_message(self, message: Message) -> Optional[Message]: """ Process a message with middleware Args: message (:obj:`.Message`): Message object to process Returns: Optional[:obj:`.Message`]: Processed message or None if discarded. """ if not self.sent_by_master(message): return message msg_text = message.text if msg_text.startswith('\\np '): message.text = msg_text[3:] return message # re_url = r'https?:\/\/\S+' # taken from django https://github.com/django/django/blob/master/django/core/validators.py#L68 valid_url = re.compile( r'(https?://)?' # scheme is validated separately r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}(?<!-)\.?)|' # domain... r'localhost|' # localhost... r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|' # ...or ipv4 r'\[?[A-F0-9]*:[A-F0-9:]+\]?)' # ...or ipv6 r'(?::\d+)?' # optional port r'(?:[/?]\S+|[/?])?', re.IGNORECASE) url = valid_url.search(msg_text) if not url: return message url = url.group(0) try: lp = LinkPreview(url) title = lp.title desc = lp.desc except Exception as e: self.logger.error("Failed to get link preview: {}".format(e)) return message text = msg_text if title or desc: text = '\n'.join([ msg_text, 'preview'.center(23, '='), title.center(23), '-' * 27, str(desc), ]) if lp.type.startswith('image') and lp.image: suffix = os.path.splitext(lp.image_url)[1] message.file = NamedTemporaryFile(suffix=suffix) message.filename = os.path.basename(message.file.name) message.file.write(lp.image) message.file.file.seek(0) message.type = MsgType.Image message.mime = lp.type message.path = message.file.name message.text = text return message