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 qq_music_wrapper(self, data, chat: Chat = None):
     efb_msg = Message()
     if data['type'] == '163':  # Netease Cloud Music
         efb_msg.type = MsgType.Text
         efb_msg.text = 'https://music.163.com/#/song?id=' + data['id']
     else:
         efb_msg.type = MsgType.Text
         efb_msg.text = data['text']
     return [efb_msg]  # todo Port for other music platform
 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_location_msg(self, msg: wxpy.Message) -> Message:
     efb_msg = Message()
     efb_msg.text = msg.text.split('\n')[0][:-1]
     efb_msg.attributes = LocationAttribute(latitude=float(msg.location['x']),
                                            longitude=float(msg.location['y']))
     efb_msg.type = MsgType.Location
     return efb_msg
 def qq_text_simple_wrapper(self, text: str, ats: dict):  # This cute function only accepts string!
     efb_msg = Message()
     efb_msg.type = MsgType.Text
     efb_msg.text = text
     if ats:  # This is used to replace specific text with @blahblah
         # And Milkice really requires a brain check
         efb_msg.substitutions = Substitutions(ats)
     return efb_msg
示例#6
0
    def process_audio(self, message: Message, audio: NamedTemporaryFile):
        try:
            # reply_text: str = '\n'.join(self.recognize(audio.name, self.lang))
            reply_text: str = '\n'.join(self.recognize(audio.name))
        except Exception:
            reply_text = 'Failed to recognize voice content.'
        if getattr(message, 'text', None) is None:
            message.text = ""
        message.text += reply_text
        message.text = message.text[:4000]

        # message.file = None
        message.edit = True
        message.edit_media = False
        coordinator.send_message(message)

        audio.close()
 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
示例#8
0
    def make_status_message(self,
                            msg_base: Message = None,
                            mid: MessageID = None) -> Message:
        if mid is not None:
            msg = self.message_cache[mid]
        elif msg_base is not None:
            msg = msg_base
        else:
            raise ValueError

        reply = Message(
            type=MsgType.Text,
            chat=msg.chat,
            author=msg.chat.make_system_member(uid=ChatID("filter_info"),
                                               name="Filter middleware",
                                               middleware=self),
            deliver_to=coordinator.master,
        )

        if mid:
            reply.uid = mid
        else:
            reply.uid = str(uuid.uuid4())

        status = self.filter_reason(msg)
        if not status:
            # Blue circle emoji
            status = "\U0001F535 This chat is not filtered."
        else:
            # Red circle emoji
            status = "\U0001F534 " + status

        reply.text = "Filter status for chat {chat_id} from {module_id}:\n" \
                     "\n" \
                     "{status}\n".format(
            module_id=msg.chat.module_id,
            chat_id=msg.chat.id,
            status=status
        )

        command = MessageCommand(name="%COMMAND_NAME%",
                                 callable_name="toggle_filter_by_chat_id",
                                 kwargs={
                                     "mid": reply.uid,
                                     "module_id": msg.chat.module_id,
                                     "chat_id": msg.chat.id
                                 })

        if self.is_chat_filtered_by_id(msg.chat):
            command.name = "Unfilter by chat ID"
            command.kwargs['value'] = False
        else:
            command.name = "Filter by chat ID"
            command.kwargs['value'] = True
        reply.commands = MessageCommands([command])

        return reply
 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
示例#10
0
 def edit_text_message(self,
                       message: Message,
                       reactions: bool = False,
                       commands: bool = False,
                       substitution: bool = False) -> Message:
     message.edit = True
     message.text = f"Edited {message.type.name} message {message.uid} @ {time.time_ns()}"
     message = self.attach_message_properties(message, reactions, commands,
                                              substitution)
     self.messages_sent[message.uid] = message
     coordinator.send_message(message)
     return message
    def process_message(self, message: Message) -> Optional[Message]:
        if message.type == MsgType.Text and \
            message.author.module_id.startswith("blueset.wechat") and \
            type(message.chat) == GroupChat and \
            self.match_list(message.text):

            x: int = 0
            message.text = "@ME " + message.text
            message.substitutions = Substitutions({})
            message.substitutions[(x, x + 3)] = message.chat

        return message
示例#12
0
    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)
示例#13
0
 def edit_file_like_message_text(self,
                                 message: Message,
                                 reactions: bool = False,
                                 commands: bool = False,
                                 substitution: bool = False) -> Message:
     message.text = f"Content of edited {message.type.name} message with ID {message.uid}"
     message.edit = True
     message.edit_media = False
     message = self.attach_message_properties(message, reactions, commands,
                                              substitution)
     self.messages_sent[message.uid] = message
     coordinator.send_message(message)
     return message
    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]
示例#15
0
 def edit_location_message(self,
                           message: Message,
                           reactions: bool = False,
                           commands: bool = False,
                           substitution: bool = False) -> Message:
     message.text = f"Content of edited location message with ID {message.uid}"
     message.edit = True
     message.attributes = LocationAttribute(
         latitude=random.uniform(0.0, 90.0),
         longitude=random.uniform(0.0, 90.0))
     message = self.attach_message_properties(message, reactions, commands,
                                              substitution)
     self.messages_sent[message.uid] = message
     coordinator.send_message(message)
     return message
    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
示例#17
0
    def edit_link_message(self,
                          message: Message,
                          reactions: bool = False,
                          commands: bool = False,
                          substitution: bool = False) -> Message:

        message.text = f"Content of edited link message with ID {message.uid}"
        message.edit = True
        message.attributes = LinkAttribute(
            title="EH Forwarder Bot (edited)",
            description="EH Forwarder Bot project site. (edited)",
            url="https://efb.1a23.studio/#edited")
        message = self.attach_message_properties(message, reactions, commands,
                                                 substitution)
        self.messages_sent[message.uid] = message
        coordinator.send_message(message)
        return message
示例#18
0
 def reply_message(self, message: Message, text: str):
     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.Text
     # 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.deliver_to = coordinator.master
     coordinator.send_message(r2)
 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
示例#21
0
 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
示例#22
0
    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
示例#23
0
    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