diff --git a/clients/pyauto/demo.py b/clients/pyauto/demo.py index dc50c15..3de2406 100644 --- a/clients/pyauto/demo.py +++ b/clients/pyauto/demo.py @@ -4,8 +4,7 @@ import logging from time import sleep -from wcfauto import Register -from wcferry import Wcf, WxMsg +from wcfauto import Register, Wcf, WxMsg logging.basicConfig(level='DEBUG', format="%(asctime)s %(message)s") LOG = logging.getLogger("Demo") diff --git a/clients/pyauto/wcfauto/__init__.py b/clients/pyauto/wcfauto/__init__.py index c28072f..e54b391 100644 --- a/clients/pyauto/wcfauto/__init__.py +++ b/clients/pyauto/wcfauto/__init__.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- from wcfauto.auto_res import Register +from wcfauto.wcf import WcfV2 as Wcf +from wcfauto.wcf import WxMsgV2 as WxMsg __version__ = "39.0.3.0" diff --git a/clients/pyauto/wcfauto/auto_res/bot.py b/clients/pyauto/wcfauto/auto_res/bot.py index b6cc139..d03bef8 100644 --- a/clients/pyauto/wcfauto/auto_res/bot.py +++ b/clients/pyauto/wcfauto/auto_res/bot.py @@ -5,7 +5,7 @@ from abc import abstractmethod from typing import Any, Callable from wcfauto.event import Event -from wcferry.client import Wcf +from wcfauto.wcf import WcfV2 as Wcf class Register(Event): diff --git a/clients/pyauto/wcfauto/auto_res/core.py b/clients/pyauto/wcfauto/auto_res/core.py index c85168f..ce2b666 100644 --- a/clients/pyauto/wcfauto/auto_res/core.py +++ b/clients/pyauto/wcfauto/auto_res/core.py @@ -7,8 +7,8 @@ import traceback from threading import Thread from typing import Any, Callable -from wcferry.client import Wcf -from wcferry.wxmsg import WxMsg +from wcfauto.wcf import WcfV2 as Wcf +from wcfauto.wcf import WxMsgV2 as WxMsg def load_function(cls): @@ -52,8 +52,8 @@ def _processing_async_func(self, async def __async_func(bot: Wcf, message: WxMsg): try: # 判断被装饰函数是否为协程函数, 本函数要求是协程函数 - if not asyncio.iscoroutinefunction(func): raise ValueError( - f'这里应使用协程函数, 而被装饰函数-> ({func.__name__}) <-是非协程函数') + if not asyncio.iscoroutinefunction(func): + raise ValueError(f'这里应使用协程函数, 而被装饰函数-> ({func.__name__}) <-是非协程函数') if message.is_pyq() and isPyq: return await func(bot, message) if not isDivision: @@ -79,8 +79,9 @@ def _processing_universal_func(self, def universal_func(bot: Wcf, message: WxMsg): try: # 判断被装饰函数是否为协程函数, 本函数要求是协程函数 - if asyncio.iscoroutinefunction(func): raise ValueError( - f'这里应使用非协程函数, 而被装饰函数-> ({func.__name__}) <-协程函数') + if asyncio.iscoroutinefunction(func): + raise ValueError( + f'这里应使用非协程函数, 而被装饰函数-> ({func.__name__}) <-协程函数') if message.is_pyq() and isPyq: return func(bot, message) if not isDivision: @@ -116,5 +117,6 @@ def run(self, *args, **kwargs): self._LOG.debug("开始接受消息") self._wcf.keep_running() + def stop_receiving(self): return self._wcf.disable_recv_msg() diff --git a/clients/pyauto/wcfauto/wcf.py b/clients/pyauto/wcfauto/wcf.py new file mode 100644 index 0000000..846469b --- /dev/null +++ b/clients/pyauto/wcfauto/wcf.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +import re +import time + +from wcferry import Wcf, WxMsg + + +class WcfV2(Wcf): + def __init__(self, host: str, port: int = 10086, debug: bool = True) -> None: + super().__init__(host, port, debug) + + def get_msg(self, block=True) -> WxMsg: + """从消息队列中获取消息 + + Args: + block (bool): 是否阻塞,默认阻塞 + + Returns: + WxMsg: 微信消息 + + Raises: + Empty: 如果阻塞并且超时,抛出空异常,需要用户自行捕获 + """ + return WxMsgV2(self.msgQ.get(block, timeout=1)) + + +class WxMsgV2(WxMsg): + """微信消息 + Attributes: + type (int): 消息类型,可通过 `get_msg_types` 获取 + id (str): 消息 id + xml (str): 消息 xml 部分 + sender (str): 消息发送人 + roomid (str): (仅群消息有)群 id + content (str): 消息内容 + thumb (str): 视频或图片消息的缩略图路径 + extra (str): 视频或图片消息的路径 + """ + + def __init__(self, msg: WxMsg) -> None: + # self._is_self = msg._is_self + # self._is_group = msg._is_group + self._type = msg.type + self._id = msg.id + self._ts = msg.ts + self._sign = msg.sign + self._xml = msg.xml + self._sender = msg.sender + self._roomid = msg.roomid + self._content = msg.content + self._thumb = msg.thumb + self._extra = msg.extra + self.__data = {'isSelf': True if self._is_self else False, + 'isGroup': True if self._is_group else False, + 'isPyq': True if self._type == 0 else False, + 'data': { + 'type': self._type, + 'content': self._content, + 'sender': self._sender, + 'msgid': self._id, + 'roomid': self._roomid if self._roomid else None, + 'xml': self._xml, + 'thumb': self._thumb if self._thumb else None, + 'extra': self._extra if self._extra else None, + 'time': int(time.time() * 1000), + }, 'revokmsgid': None, 'isRevokeMsg': False, } + self.__revokmsg_p() + + def __revokmsg_p(self): + rmsg = self.__data['data']['content'] + rev_type = re.findall('", rmsg) + if len(rev_type) == 0 or len(rev_w) == 0: + return + if rev_type[0] == 'revokemsg' and rev_w[0] == '你撤回了一条消息': + self.__data['data']['content'] = rev_w[0] + self.__data['isRevokeMsg'] = True + self.__data['revokmsgid'] = re.findall('(.*?)', rmsg)[0] + + def __str__(self) -> str: + return repr(self.__data) + + def __repr__(self) -> str: + return repr(self.__data) + + def __getitem__(self, key): + return self.__data[key] + + def __getattr__(self, item): + if item in ['content', 'sender', 'roomid', 'xml', 'thumb', 'extra', 'type']: + return self.__data['data'][item] + if item == 'id': + return self.__data['data']['msgid'] + if item == 'ts': + return self._ts + if item == 'sign': + return self._sign + + def __setitem__(self, key, value): + self.__data[key] = value + + def is_image(self) -> bool: + """是否是图片""" + return self.type == 3 and ('imgdatahash' in self.__data['data']['content']) + + def is_voice(self) -> bool: + """是否是语音""" + return self.type == 34 and ('voicemsg' in self.__data['data']['content']) + + def is_video(self) -> bool: + """是否是视频""" + return self.type == 43 and ('videomsg' in self.__data['data']['content']) + + def is_pyq(self) -> bool: + return self.type == 0 + + def from_self(self) -> bool: + """是否自己发的消息""" + return self._is_self == 1 + + def from_group(self) -> bool: + """是否群聊消息""" + return self._is_group + + def is_at(self, wxid) -> bool: + """是否被 @:群消息,在 @ 名单里,并且不是 @ 所有人""" + if not self.from_group(): + return False # 只有群消息才能 @ + + if not re.findall(f".*({wxid}).*", self.xml): + return False # 不在 @ 清单里 + + if re.findall(r"@(?:所有人|all|All)", self.content): + return False # 排除 @ 所有人 + + return True + + def is_text(self) -> bool: + """是否文本消息""" + return self.type == 1