feat: 微信自动化客服(wechatauto-replica) 干净历史导入 - AI 自动回复/语音收发/朋友圈发布
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
from .base import Message, BaseMessage, HumanMessage
|
||||
from .mattr import SystemMessage, FriendMessage, SelfMessage
|
||||
from .mtype import (
|
||||
TextMessage,
|
||||
QuoteMessage,
|
||||
VoiceMessage,
|
||||
ImageMessage,
|
||||
VideoMessage,
|
||||
FileMessage,
|
||||
LinkMessage,
|
||||
LocationMessage,
|
||||
PersonalCardMessage,
|
||||
OtherMessage,
|
||||
)
|
||||
from .friend import (
|
||||
FriendTextMessage,
|
||||
FriendQuoteMessage,
|
||||
FriendImageMessage,
|
||||
FriendFileMessage,
|
||||
FriendVideoMessage,
|
||||
FriendVoiceMessage,
|
||||
FriendLinkMessage,
|
||||
FriendLocationMessage,
|
||||
FriendPersonalCardMessage,
|
||||
FriendOtherMessage,
|
||||
)
|
||||
from .self import (
|
||||
SelfTextMessage,
|
||||
SelfQuoteMessage,
|
||||
SelfImageMessage,
|
||||
SelfFileMessage,
|
||||
SelfVideoMessage,
|
||||
SelfVoiceMessage,
|
||||
SelfLinkMessage,
|
||||
SelfLocationMessage,
|
||||
SelfPersonalCardMessage,
|
||||
SelfOtherMessage,
|
||||
)
|
||||
from .msg import parse_msg, parse_msg_attr, parse_msg_type
|
||||
|
||||
__all__ = [
|
||||
"Message",
|
||||
"BaseMessage",
|
||||
"HumanMessage",
|
||||
"SystemMessage",
|
||||
"FriendMessage",
|
||||
"SelfMessage",
|
||||
"TextMessage",
|
||||
"QuoteMessage",
|
||||
"VoiceMessage",
|
||||
"ImageMessage",
|
||||
"VideoMessage",
|
||||
"FileMessage",
|
||||
"LinkMessage",
|
||||
"LocationMessage",
|
||||
"PersonalCardMessage",
|
||||
"OtherMessage",
|
||||
"FriendTextMessage",
|
||||
"FriendQuoteMessage",
|
||||
"FriendImageMessage",
|
||||
"FriendFileMessage",
|
||||
"FriendVideoMessage",
|
||||
"FriendVoiceMessage",
|
||||
"FriendLinkMessage",
|
||||
"FriendLocationMessage",
|
||||
"FriendPersonalCardMessage",
|
||||
"FriendOtherMessage",
|
||||
"SelfTextMessage",
|
||||
"SelfQuoteMessage",
|
||||
"SelfImageMessage",
|
||||
"SelfFileMessage",
|
||||
"SelfVideoMessage",
|
||||
"SelfVoiceMessage",
|
||||
"SelfLinkMessage",
|
||||
"SelfLocationMessage",
|
||||
"SelfPersonalCardMessage",
|
||||
"SelfOtherMessage",
|
||||
"parse_msg",
|
||||
"parse_msg_attr",
|
||||
"parse_msg_type",
|
||||
]
|
||||
@@ -0,0 +1,285 @@
|
||||
from wechatauto import uia
|
||||
from wechatauto.ui.component import (
|
||||
Menu,
|
||||
SelectContactWnd
|
||||
)
|
||||
from wechatauto.utils import uilock
|
||||
from wechatauto.param import WxParam, WxResponse, PROJECT_NAME
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import (
|
||||
Dict,
|
||||
List,
|
||||
Union,
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
Iterator,
|
||||
Tuple
|
||||
)
|
||||
from hashlib import md5
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from wechatauto.ui.chatbox import ChatBox
|
||||
|
||||
|
||||
def truncate_string(s: str, n: int = 8) -> str:
|
||||
s = s.replace('\n', '').strip()
|
||||
return s if len(s) <= n else s[:n] + '...'
|
||||
|
||||
|
||||
class Message:
|
||||
"""消息对象基类
|
||||
|
||||
该类不会直接实例化,而是作为所有消息类型的基类提供
|
||||
常用的工具方法。实际的属性均由子类在 ``__init__`` 中
|
||||
动态注入。
|
||||
"""
|
||||
|
||||
_EXCLUDE_FIELDS = {"control", "parent", "root"}
|
||||
|
||||
# region --- 迭代/映射相关 -------------------------------------------------
|
||||
def _iter_public_items(self) -> Iterator[Tuple[str, Any]]:
|
||||
"""遍历当前消息可公开的字段"""
|
||||
|
||||
if not hasattr(self, "__dict__"):
|
||||
return
|
||||
|
||||
seen = set()
|
||||
for key, value in self.__dict__.items():
|
||||
if key.startswith("_") or key in self._EXCLUDE_FIELDS:
|
||||
continue
|
||||
if key == "hash" and not WxParam.MESSAGE_HASH:
|
||||
continue
|
||||
seen.add(key)
|
||||
yield key, value
|
||||
|
||||
# 补充类级字段(如 type/attr),保证 to_dict/match 可用
|
||||
for key in ('type', 'attr'):
|
||||
if key in seen:
|
||||
continue
|
||||
if any(key in cls.__dict__ for cls in type(self).__mro__):
|
||||
yield key, getattr(self, key)
|
||||
|
||||
def __iter__(self) -> Iterator[str]:
|
||||
for key, _ in self._iter_public_items():
|
||||
yield key
|
||||
|
||||
def __len__(self) -> int:
|
||||
return sum(1 for _ in self._iter_public_items())
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
for key, value in self._iter_public_items():
|
||||
if key == item:
|
||||
return value
|
||||
raise KeyError(item)
|
||||
|
||||
def __contains__(self, key: object) -> bool:
|
||||
if not isinstance(key, str):
|
||||
return False
|
||||
return any(field == key for field, _ in self._iter_public_items())
|
||||
|
||||
# endregion ----------------------------------------------------------------
|
||||
|
||||
# region --- 字段访问 -------------------------------------------------------
|
||||
def keys(self) -> Tuple[str, ...]:
|
||||
return tuple(key for key, _ in self._iter_public_items())
|
||||
|
||||
def values(self) -> Tuple[Any, ...]:
|
||||
return tuple(value for _, value in self._iter_public_items())
|
||||
|
||||
def items(self) -> Tuple[Tuple[str, Any], ...]:
|
||||
return tuple(self._iter_public_items())
|
||||
|
||||
def get(self, key: str, default: Any = None) -> Any:
|
||||
for field, value in self._iter_public_items():
|
||||
if field == key:
|
||||
return value
|
||||
return default
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return dict(self._iter_public_items())
|
||||
|
||||
def copy(self) -> Dict[str, Any]:
|
||||
return self.to_dict().copy()
|
||||
|
||||
# endregion ----------------------------------------------------------------
|
||||
|
||||
# region --- 状态判断 -------------------------------------------------------
|
||||
def match(self, **conditions: Any) -> bool:
|
||||
"""判断当前消息是否同时满足给定的字段条件"""
|
||||
|
||||
data = self.to_dict()
|
||||
return all(data.get(key) == value for key, value in conditions.items())
|
||||
|
||||
@property
|
||||
def is_self(self) -> bool:
|
||||
return getattr(self, "attr", None) == "self"
|
||||
|
||||
@property
|
||||
def is_friend(self) -> bool:
|
||||
return getattr(self, "attr", None) == "friend"
|
||||
|
||||
@property
|
||||
def is_system(self) -> bool:
|
||||
return getattr(self, "attr", None) == "system"
|
||||
|
||||
# endregion ----------------------------------------------------------------
|
||||
|
||||
# region --- 魔术方法 -------------------------------------------------------
|
||||
def __str__(self) -> str:
|
||||
content = getattr(self, "content", None)
|
||||
if content is None:
|
||||
return super().__str__()
|
||||
return str(content)
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Message):
|
||||
return NotImplemented
|
||||
|
||||
self_id = getattr(self, "id", None)
|
||||
other_id = getattr(other, "id", None)
|
||||
if self_id is not None and other_id is not None:
|
||||
return self_id == other_id
|
||||
|
||||
if WxParam.MESSAGE_HASH:
|
||||
return getattr(self, "hash", None) == getattr(other, "hash", None)
|
||||
|
||||
return self is other
|
||||
|
||||
def __hash__(self) -> int:
|
||||
msg_id = getattr(self, "id", None)
|
||||
if msg_id is not None:
|
||||
return hash(msg_id)
|
||||
|
||||
if WxParam.MESSAGE_HASH:
|
||||
return hash(getattr(self, "hash", None))
|
||||
|
||||
return super().__hash__()
|
||||
|
||||
# endregion ----------------------------------------------------------------
|
||||
|
||||
class BaseMessage(Message, ABC):
|
||||
type: str = 'base'
|
||||
attr: str = 'base'
|
||||
control: uia.Control
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
self.parent = parent
|
||||
self.control = control
|
||||
self.direction = additonal_attr.get('direction', None)
|
||||
self.distince = additonal_attr.get('direction_distence', None)
|
||||
self.root = parent.root
|
||||
self.id = self.control.runtimeid
|
||||
self.content = self.control.Name
|
||||
rect = self.control.BoundingRectangle
|
||||
self.hash_text = f'({rect.height()},{rect.width()}){self.content}'
|
||||
self.hash = md5(self.hash_text.encode()).hexdigest()
|
||||
|
||||
def __repr__(self):
|
||||
cls_name = self.__class__.__name__
|
||||
content = truncate_string(self.content)
|
||||
return f"<{PROJECT_NAME} - {cls_name}({content}) at {hex(id(self))}>"
|
||||
|
||||
def roll_into_view(self):
|
||||
if not self.exists():
|
||||
return WxResponse.failure('消息目标控件不存在,无法滚动至显示窗口')
|
||||
if uia.RollIntoView(
|
||||
self.parent.msgbox,
|
||||
self.control
|
||||
) == 'not exist':
|
||||
return WxResponse.failure('消息目标控件不存在,无法滚动至显示窗口')
|
||||
return WxResponse.success('成功')
|
||||
|
||||
def exists(self):
|
||||
if self.control.Exists(0) and self.control.BoundingRectangle.height() > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class HumanMessage(BaseMessage, ABC):
|
||||
attr = 'human'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
@abstractmethod
|
||||
def _click(self, x, y, right=False): ...
|
||||
|
||||
@abstractmethod
|
||||
def _bias(self): ...
|
||||
|
||||
def click(self):
|
||||
self._click(right=False, x=self._bias * 2, y=WxParam.DEFAULT_MESSAGE_YBIAS)
|
||||
|
||||
def right_click(self):
|
||||
self._click(right=True, x=self._bias, y=WxParam.DEFAULT_MESSAGE_YBIAS)
|
||||
|
||||
@uilock
|
||||
def select_option(self, option: str, timeout=2) -> WxResponse:
|
||||
if not self.exists():
|
||||
return WxResponse.failure('消息对象已失效')
|
||||
self._click(right=True, x=self._bias * 2, y=WxParam.DEFAULT_MESSAGE_YBIAS)
|
||||
if menu := Menu(self, timeout):
|
||||
return menu.select(option)
|
||||
else:
|
||||
return WxResponse.failure('操作失败')
|
||||
|
||||
@uilock
|
||||
def forward(
|
||||
self,
|
||||
targets: Union[List[str], str],
|
||||
timeout: int = 3,
|
||||
interval: float = 0.1
|
||||
) -> WxResponse:
|
||||
"""转发消息
|
||||
|
||||
Args:
|
||||
targets (Union[List[str], str]): 目标用户列表
|
||||
timeout (int, optional): 超时时间,单位为秒,若为None则不启用超时设置
|
||||
interval (float): 选择联系人时间间隔
|
||||
|
||||
Returns:
|
||||
WxResponse: 调用结果
|
||||
"""
|
||||
if not self.exists():
|
||||
return WxResponse.failure('消息对象已失效')
|
||||
if not self.select_option('转发...', timeout=timeout):
|
||||
return WxResponse.failure('当前消息无法转发')
|
||||
|
||||
select_wnd = SelectContactWnd(self)
|
||||
return select_wnd.send(targets, interval=interval)
|
||||
|
||||
@uilock
|
||||
def quote(
|
||||
self, text: str,
|
||||
at: Union[List[str], str] = None,
|
||||
timeout: int = 3
|
||||
) -> WxResponse:
|
||||
"""引用消息
|
||||
|
||||
Args:
|
||||
text (str): 引用内容
|
||||
at (List[str], optional): @用户列表
|
||||
timeout (int, optional): 超时时间,单位为秒,若为None则不启用超时设置
|
||||
|
||||
Returns:
|
||||
WxResponse: 调用结果
|
||||
"""
|
||||
if not self.exists():
|
||||
return WxResponse.failure('消息对象已失效')
|
||||
if not self.select_option('引用', timeout=timeout):
|
||||
return WxResponse.failure('当前消息无法引用')
|
||||
|
||||
if at:
|
||||
self.parent.input_at(at)
|
||||
|
||||
return self.parent.send_text(text)
|
||||
@@ -0,0 +1,112 @@
|
||||
from .mattr import *
|
||||
from .mtype import *
|
||||
|
||||
|
||||
class FriendTextMessage(FriendMessage, TextMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendQuoteMessage(FriendMessage, QuoteMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendImageMessage(FriendMessage, ImageMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendEmojiMessage(FriendMessage, EmojiMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendFileMessage(FriendMessage, FileMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendVideoMessage(FriendMessage, VideoMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendVoiceMessage(FriendMessage, VoiceMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendLinkMessage(FriendMessage, LinkMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendLocationMessage(FriendMessage, LocationMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendPersonalCardMessage(FriendMessage, PersonalCardMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class FriendOtherMessage(FriendMessage, OtherMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
@@ -0,0 +1,79 @@
|
||||
from .base import (
|
||||
BaseMessage,
|
||||
HumanMessage
|
||||
)
|
||||
from wechatauto import uia
|
||||
from wechatauto.param import (
|
||||
WxParam,
|
||||
WxResponse,
|
||||
PROJECT_NAME
|
||||
)
|
||||
|
||||
from typing import (
|
||||
Dict,
|
||||
List,
|
||||
Any,
|
||||
TYPE_CHECKING
|
||||
)
|
||||
if TYPE_CHECKING:
|
||||
from wechatauto.ui.chatbox import ChatBox
|
||||
|
||||
|
||||
class SystemMessage(BaseMessage):
|
||||
attr = 'system'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
self.sender = 'system'
|
||||
self.sender_remark = 'system'
|
||||
|
||||
|
||||
class FriendMessage(HumanMessage):
|
||||
attr = 'friend'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
def _click(self, x, y, right=False):
|
||||
self.roll_into_view()
|
||||
if right:
|
||||
self.control.RightClick(x=x, y=y, ratioX=0, ratioY=0)
|
||||
else:
|
||||
self.control.Click(ratioX=0, ratioY=0)
|
||||
|
||||
@property
|
||||
def _bias(self):
|
||||
return WxParam.DEFAULT_MESSAGE_XBIAS
|
||||
|
||||
|
||||
class SelfMessage(HumanMessage):
|
||||
attr = 'self'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
def _click(self, x, y, right=False):
|
||||
self.roll_into_view()
|
||||
if right:
|
||||
self.control.RightClick(x=x, y=y, ratioX=1, ratioY=0)
|
||||
else:
|
||||
self.control.Click(x=x, y=y, ratioX=1, ratioY=0)
|
||||
|
||||
@property
|
||||
def _bias(self):
|
||||
return -WxParam.DEFAULT_MESSAGE_XBIAS
|
||||
@@ -0,0 +1,140 @@
|
||||
from wechatauto.utils.tools import (
|
||||
detect_message_direction
|
||||
)
|
||||
from wechatauto import uia
|
||||
from .mattr import (
|
||||
SystemMessage,
|
||||
FriendMessage,
|
||||
SelfMessage
|
||||
)
|
||||
from .mtype import *
|
||||
from . import self as selfmsg
|
||||
from . import friend as friendmsg
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Literal,
|
||||
Dict,
|
||||
Any
|
||||
)
|
||||
import os
|
||||
import re
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from wechatauto.ui.chatbox import ChatBox
|
||||
|
||||
|
||||
def parse_msg_attr(
|
||||
control: uia.Control,
|
||||
parent: 'ChatBox'
|
||||
):
|
||||
msg_direction_hash = {
|
||||
'left': 'friend',
|
||||
'right': 'self'
|
||||
}
|
||||
if control.AutomationId:
|
||||
msg_screenshot = control.ScreenShot()
|
||||
msg_direction, msg_direction_distence = detect_message_direction(msg_screenshot)
|
||||
msg_attr = msg_direction_hash.get(msg_direction)
|
||||
os.remove(msg_screenshot)
|
||||
|
||||
additonal_attr = {
|
||||
'direction': msg_direction,
|
||||
'direction_distence': msg_direction_distence
|
||||
}
|
||||
|
||||
else:
|
||||
msg_attr = 'system'
|
||||
|
||||
if msg_attr == 'system':
|
||||
return SystemMessage(control, parent)
|
||||
elif msg_attr == 'friend':
|
||||
return parse_msg_type(control, parent, 'Friend', additonal_attr)
|
||||
elif msg_attr == 'self':
|
||||
return parse_msg_type(control, parent, 'Self', additonal_attr)
|
||||
|
||||
|
||||
def parse_msg_type(
|
||||
control: uia.Control,
|
||||
parent,
|
||||
attr: Literal['Self', 'Friend'],
|
||||
additonal_attr: Dict[str, Any]
|
||||
):
|
||||
"""
|
||||
多层次消息类型识别算法
|
||||
基于ClassName、Name等多重验证确保识别准确性
|
||||
"""
|
||||
if attr == 'Friend':
|
||||
msgtype = friendmsg
|
||||
else:
|
||||
msgtype = selfmsg
|
||||
|
||||
msg_text = control.Name
|
||||
msg_classname = control.ClassName
|
||||
msg_automation_id = control.AutomationId
|
||||
|
||||
# 第一层:ClassName强特征识别(最可靠)
|
||||
classname_result = _classify_by_classname(msg_classname)
|
||||
if classname_result:
|
||||
return getattr(msgtype, f'{attr}{classname_result}')(control, parent, additonal_attr)
|
||||
|
||||
# 第二层:基于ClassName分类后的详细识别
|
||||
if msg_classname == "mmui::ChatBubbleItemView":
|
||||
# Name前缀特征识别
|
||||
prefix_result = _classify_by_name_prefix(msg_text)
|
||||
if prefix_result:
|
||||
return getattr(msgtype, f'{attr}{prefix_result}')(control, parent, additonal_attr)
|
||||
|
||||
# 图片消息处理
|
||||
if msg_text == '图片':
|
||||
return getattr(msgtype, f'{attr}ImageMessage')(control, parent, additonal_attr)
|
||||
|
||||
# 如果都不匹配,归类为其他消息
|
||||
return getattr(msgtype, f'{attr}OtherMessage')(control, parent, additonal_attr)
|
||||
|
||||
elif msg_classname == "mmui::ChatTextItemView":
|
||||
# 第三层:引用消息处理
|
||||
if _is_quote_message(msg_text):
|
||||
return getattr(msgtype, f'{attr}QuoteMessage')(control, parent, additonal_attr)
|
||||
else:
|
||||
return getattr(msgtype, f'{attr}TextMessage')(control, parent, additonal_attr)
|
||||
|
||||
return getattr(msgtype, f'{attr}OtherMessage')(control, parent, additonal_attr)
|
||||
|
||||
|
||||
def _classify_by_classname(classname: str) -> str:
|
||||
classname_mapping = {
|
||||
"mmui::ChatVoiceItemView": "VoiceMessage",
|
||||
"mmui::ChatPersonalCardItemView": "PersonalCardMessage",
|
||||
}
|
||||
return classname_mapping.get(classname, "")
|
||||
|
||||
|
||||
def _classify_by_name_prefix(name: str) -> str:
|
||||
if name.startswith("[动画表情]") or name.startswith("[动画表情"):
|
||||
return "EmojiMessage"
|
||||
|
||||
if name.startswith("[链接]"):
|
||||
return "LinkMessage"
|
||||
|
||||
elif name.startswith("位置"):
|
||||
return "LocationMessage"
|
||||
|
||||
elif name.startswith("文件\n"):
|
||||
return "FileMessage"
|
||||
|
||||
elif name.startswith("视频"):
|
||||
return "VideoMessage"
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def _is_quote_message(name: str) -> bool:
|
||||
quote_pattern = r'^(.*?)\s*\n引用\s+(.+?)\s+的消息\s*:\s*(.*)$'
|
||||
return bool(re.search(quote_pattern, name, re.DOTALL))
|
||||
|
||||
|
||||
def parse_msg(
|
||||
control: uia.Control,
|
||||
parent
|
||||
):
|
||||
return parse_msg_attr(control, parent)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,112 @@
|
||||
from .mattr import *
|
||||
from .mtype import *
|
||||
|
||||
|
||||
class SelfTextMessage(SelfMessage, TextMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfQuoteMessage(SelfMessage, QuoteMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfImageMessage(SelfMessage, ImageMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfEmojiMessage(SelfMessage, EmojiMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfFileMessage(SelfMessage, FileMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfVideoMessage(SelfMessage, VideoMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfVoiceMessage(SelfMessage, VoiceMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfLinkMessage(SelfMessage, LinkMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfLocationMessage(SelfMessage, LocationMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfPersonalCardMessage(SelfMessage, PersonalCardMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
|
||||
|
||||
class SelfOtherMessage(SelfMessage, OtherMessage):
|
||||
def __init__(
|
||||
self,
|
||||
control: uia.Control,
|
||||
parent: "ChatBox",
|
||||
additonal_attr: Dict[str, Any] = {}
|
||||
):
|
||||
super().__init__(control, parent, additonal_attr)
|
||||
Reference in New Issue
Block a user