def wechat_raw_link_msg(self, msg: wxpy.Message, title: str, description: str, image: str, url: str, suffix: str = "") -> Message: if url: efb_msg = Message(type=MsgType.Link, text=suffix, attributes=LinkAttribute(title=title, description=description, image=image, url=url)) else: efb_msg = Message( type=MsgType.Text, text=f"{title}\n{description}", ) if suffix: efb_msg.text += f"\n{suffix}" if image: efb_msg.text += f"\n\n{image}" return efb_msg
def qq_location_wrapper(self, data, chat: Chat = None): efb_msg = Message(text=data['content'], type=MsgType.Location, attributes=LocationAttribute( longitude=float(data['lon']), latitude=float(data['lat']))) return [efb_msg]
def send_text_message(self, chat: Chat, author: Optional[ChatMember] = None, target: Optional[Message] = None, reactions: bool = False, commands: bool = False, substitution: bool = False, unsupported: bool = False) -> Message: """Send a text message to master channel. Leave author blank to use “self” of the chat. Returns the message sent. """ author = author or chat.self uid = f"__msg_id_{uuid4()}__" msg_type = MsgType.Unsupported if unsupported else MsgType.Text message = Message( chat=chat, author=author, type=msg_type, target=target, uid=uid, text=f"Content of {msg_type.name} message with ID {uid}", deliver_to=coordinator.master) message = self.attach_message_properties(message, reactions, commands, substitution) coordinator.send_message(message) self.messages_sent[uid] = message return message
def send_file_like_message(self, msg_type: MsgType, file_path: Path, mime: str, chat: Chat, author: Optional[ChatMember] = None, target: Optional[Message] = None, reactions: bool = False, commands: bool = False, substitution: bool = False) -> Message: author = author or chat.self uid = f"__msg_id_{uuid4()}__" message = Message( chat=chat, author=author, type=msg_type, target=target, uid=uid, file=file_path.open('rb'), filename=file_path.name, path=file_path, mime=mime, text=f"Content of {msg_type.name} message with ID {uid}", deliver_to=coordinator.master) message = self.attach_message_properties(message, reactions, commands, substitution) self.messages_sent[uid] = message coordinator.send_message(message) return message
def exit_callback(self): # Don't send prompt if there's nowhere to send. if not getattr(coordinator, 'master', None): raise Exception( self._("Web WeChat logged your account out before master channel is ready.")) self.logger.debug('Calling exit callback...') if self._stop_polling_event.is_set(): return msg = Message( chat=self.user_auth_chat, author=self.user_auth_chat.other, deliver_to=coordinator.master, text=self._( "WeChat server has logged you out. Please log in again when you are ready."), uid=f"__reauth__.{uuid4()}", type=MsgType.Text, ) on_log_out = self.flag("on_log_out") on_log_out = on_log_out if on_log_out in ( "command", "idle", "reauth") else "command" if on_log_out == "command": msg.type = MsgType.Text msg.commands = MessageCommands( [MessageCommand(name=self._("Log in again"), callable_name="reauth", kwargs={"command": True})]) elif on_log_out == "reauth": if self.flag("qr_reload") == "console_qr_code": msg.text += "\n" + self._("Please check your log to continue.") self.reauth() coordinator.send_message(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 send_status_message(self, status: StatusAttribute, chat: Chat, author: Optional[ChatMember] = None, target: Optional[Message] = None) -> Message: """Send a status message to master channel. Leave author blank to use “self” of the chat. Returns the message sent. """ author = author or chat.self uid = f"__msg_id_{uuid4()}__" message = Message(chat=chat, author=author, type=MsgType.Status, target=target, uid=uid, text="", attributes=status, deliver_to=coordinator.master) coordinator.send_message(message) self.messages_sent[uid] = message return message
def send_link_message(self, chat: Chat, author: Optional[ChatMember] = None, target: Optional[Message] = None, reactions: bool = False, commands: bool = False, substitution: bool = False) -> Message: author = author or chat.self uid = f"__msg_id_{uuid4()}__" message = Message(chat=chat, author=author, type=MsgType.Link, target=target, uid=uid, text=f"Content of link message with ID {uid}", attributes=LinkAttribute( title="EH Forwarder Bot", description="EH Forwarder Bot project site.", url="https://efb.1a23.studio"), deliver_to=coordinator.master) message = self.attach_message_properties(message, reactions, commands, substitution) self.messages_sent[uid] = message coordinator.send_message(message) return message
def wechat_text_msg(self, msg: wxpy.Message) -> Optional[Message]: if msg.chat.user_name == "newsapp" and msg.text.startswith("<mmreader>"): return self.wechat_newsapp_msg(msg) if msg.text.startswith("http://weixin.qq.com/cgi-bin/redirectforward?args="): return self.wechat_location_msg(msg) chat, author = self.get_chat_and_author(msg) if self.channel.flag("text_post_processing"): text = ews_utils.wechat_string_unescape(msg.text) else: text = msg.text or "" efb_msg = Message( chat=chat, author=author, text=text, type=MsgType.Text ) if msg.is_at and chat.self: found = False for i in re.finditer(r"@([^@\s]*)(?=\u2005|$|\s)", msg.text): if i.groups()[0] in (self.bot.self.name, msg.chat.self.display_name): found = True efb_msg.substitutions = Substitutions({ i.span(): chat.self }) if not found: append = "@" + self.bot.self.name efb_msg.substitutions = Substitutions({ (len(msg.text) + 1, len(msg.text) + 1 + len(append)): chat.self }) efb_msg.text += " " + append return efb_msg
def wechat_system_msg(self, msg: wxpy.Message) -> Optional[Message]: if msg.recalled_message_id: recall_id = str(msg.recalled_message_id) # check conversion table first if recall_id in self.recall_msg_id_conversion: # prevent feedback of messages deleted by master channel. del self.recall_msg_id_conversion[recall_id] return None # val = self.recall_msg_id_conversion.pop(recall_id) # val[1] -= 1 # if val[1] > 0: # not all associated messages are recalled. # return None # else: # efb_msg.uid = val[0] else: # Format message IDs as JSON of List[List[str]]. chat, author = self.get_chat_and_author(msg) efb_msg = Message( chat=chat, author=author, uid=MessageID(json.dumps([[recall_id]])) ) coordinator.send_status(MessageRemoval(source_channel=self.channel, destination_channel=coordinator.master, message=efb_msg)) return None chat, _ = self.get_chat_and_author(msg) try: author = chat.get_member(SystemChatMember.SYSTEM_ID) except KeyError: author = chat.add_system_member() if any(i in msg.text for i in self.NEW_CHAT_PATTERNS): coordinator.send_status(ChatUpdates( channel=self.channel, new_chats=(chat.uid,) )) elif any(i in msg.text for i in self.CHAT_AND_MEMBER_UPDATE_PATTERNS): # TODO: detect actual member changes from message text coordinator.send_status(ChatUpdates( channel=self.channel, modified_chats=(chat.uid,) )) return Message( text=msg.text, type=MsgType.Text, chat=chat, author=author, )
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_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
def qq_contact_wrapper(self, data, chat: Chat = None): uid = data['id'] contact_type = data['type'] efb_msg = Message( type=MsgType.Text, text=self._("Chat Recommendation Received\nID: {}\nType: {}").format(uid, contact_type) ) return [efb_msg]
def qq_sign_wrapper(self, data, chat: Chat = None): location = self._('at {}').format(data['location']) if 'location' in data else self._('at Unknown Place') title = '' if 'title' not in data else (self._('with title {}').format(data['title'])) efb_msg = Message( type=MsgType.Text, text=self._('signed in {location} {title}').format(title=title, location=location) ) return [efb_msg]
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 base_message(chat, master_channel): msg = Message( deliver_to=master_channel, author=chat.other, chat=chat, text="Message", uid="0", ) return msg
def test_verify_missing_chat(chat): with pytest.raises(AssertionError): msg = Message( deliver_to=coordinator.master, author=chat.other, type=MsgType.Text, text="Message", ) msg.verify()
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_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 test_verify_missing_author(chat, master_channel): with pytest.raises(AssertionError): msg = Message( deliver_to=master_channel, chat=chat, type=MsgType.Text, text="Message", ) msg.verify()
def test_verify_text_msg(chat): msg = Message( deliver_to=coordinator.master, author=chat.other, chat=chat, type=MsgType.Text, text="Message", ) msg.verify()
def test_verify_missing_deliver_to(chat): msg = Message( author=chat.other, chat=chat, type=MsgType.Text, text="Message", ) with pytest.raises(AssertionError): msg.verify()
def wechat_system_unsupported_msg(self, msg: wxpy.Message) -> Optional[Message]: if msg.raw['MsgType'] in (50, 52, 53): text = self._("[Incoming audio/video call, please check your phone.]") else: return None efb_msg = Message( text=text, type=MsgType.Unsupported, ) return efb_msg
def qq_share_wrapper(self, data, chat: Chat = None): efb_msg = Message( type=MsgType.Link, text='', attributes=LinkAttribute( title='' if 'title' not in data else data['title'], description='' if 'content' not in data else data['content'], image='' if 'image' not in data else data['image'], url=data['url'])) return [efb_msg]
def send_message_recall_status(self): slave = next(iter(coordinator.slaves.values())) alice = slave.get_chat('alice') msg = Message( deliver_to=slave, chat=alice, author=alice.self, uid="1" ) status = MessageRemoval(self, slave, msg) return coordinator.send_status(status)
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 send_text_msg(self): slave = next(iter(coordinator.slaves.values())) wonderland = slave.get_chat('wonderland001') msg = Message( deliver_to=slave, chat=wonderland, author=wonderland.self, type=MsgType.Text, text="Hello, world.", ) return coordinator.send_message(msg)
def test_verify_different_author_and_chat(patch_chat, patch_chat_member, master_channel): msg = Message( chat=patch_chat, author=patch_chat_member, text="Message", deliver_to=master_channel ) msg.verify() patch_chat.verify.assert_called_once() patch_chat_member.verify.assert_called_once()
def test_pickle_message_removal(slave_channel, master_channel): msg = Message() msg.chat = slave_channel.alice msg.uid = "uid" msg_removal = MessageRemoval(source_channel=master_channel, destination_channel=slave_channel, message=msg) msg_removal_dup = pickle.loads(pickle.dumps(msg_removal)) # Assume Message is picklable for i in ("source_channel", "destination_channel"): assert getattr(msg_removal, i) == getattr(msg_removal_dup, i)
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